diff options
302 files changed, 5967 insertions, 2055 deletions
diff --git a/Cargo.lock b/Cargo.lock index 7ea3a6fd9c3..1a16789d15c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -555,7 +555,7 @@ dependencies = [ [[package]] name = "clippy" -version = "0.1.57" +version = "0.1.58" dependencies = [ "cargo_metadata 0.12.0", "clippy_lints", @@ -582,6 +582,7 @@ version = "0.0.1" dependencies = [ "bytecount", "clap", + "indoc", "itertools 0.10.1", "opener", "regex", @@ -591,7 +592,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.57" +version = "0.1.58" dependencies = [ "cargo_metadata 0.12.0", "clippy_utils", @@ -612,7 +613,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.57" +version = "0.1.58" dependencies = [ "if_chain", "rustc-semver", @@ -1666,6 +1667,15 @@ dependencies = [ ] [[package]] +name = "indoc" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136" +dependencies = [ + "unindent", +] + +[[package]] name = "installer" version = "0.0.0" dependencies = [ @@ -2144,6 +2154,20 @@ dependencies = [ ] [[package]] +name = "measureme" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd460fad6e55ca82fa0cd9dab0d315294188fd9ec6efbf4105e5635d4872ef9c" +dependencies = [ + "log", + "memmap2", + "parking_lot", + "perf-event-open-sys", + "rustc-hash", + "smallvec", +] + +[[package]] name = "memchr" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2247,7 +2271,7 @@ dependencies = [ "hex 0.4.2", "libc", "log", - "measureme", + "measureme 9.1.2", "rand 0.8.4", "rustc-workspace-hack", "rustc_version 0.4.0", @@ -3235,7 +3259,7 @@ dependencies = [ "indexmap", "jobserver", "libc", - "measureme", + "measureme 9.1.2", "memmap2", "parking_lot", "rustc-ap-rustc_graphviz", @@ -3674,7 +3698,7 @@ dependencies = [ "bitflags", "cstr", "libc", - "measureme", + "measureme 10.0.0", "rustc-demangle", "rustc_arena", "rustc_ast", @@ -3767,7 +3791,7 @@ dependencies = [ "indexmap", "jobserver", "libc", - "measureme", + "measureme 10.0.0", "memmap2", "parking_lot", "rustc-hash", @@ -4292,7 +4316,7 @@ dependencies = [ name = "rustc_query_impl" version = "0.0.0" dependencies = [ - "measureme", + "measureme 10.0.0", "rustc-rayon-core", "rustc_ast", "rustc_data_structures", @@ -4305,7 +4329,6 @@ dependencies = [ "rustc_serialize", "rustc_session", "rustc_span", - "tracing", ] [[package]] @@ -5581,6 +5604,12 @@ dependencies = [ ] [[package]] +name = "unindent" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" + +[[package]] name = "unstable-book-gen" version = "0.1.0" dependencies = [ diff --git a/RELEASES.md b/RELEASES.md index 52d823d8aca..f1584224656 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -77,7 +77,7 @@ Cargo - [Cargo supports specifying a minimum supported Rust version in Cargo.toml.][`rust-version`] This has no effect at present on dependency version selection. We encourage crates to specify their minimum supported Rust version, and we encourage CI systems - that support Rust code to include a crate's specified minimum version in the text matrix for that + that support Rust code to include a crate's specified minimum version in the test matrix for that crate by default. Compatibility notes diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index f673ab2f3ef..74def2bab1b 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -37,9 +37,7 @@ pub trait MutVisitor: Sized { /// Mutable token visiting only exists for the `macro_rules` token marker and should not be /// used otherwise. Token visitor would be entirely separate from the regular visitor if /// the marker didn't have to visit AST fragments in nonterminal tokens. - fn token_visiting_enabled(&self) -> bool { - false - } + const VISIT_TOKENS: bool = false; // Methods in this trait have one of three forms: // @@ -363,7 +361,7 @@ pub fn visit_mac_args<T: MutVisitor>(args: &mut MacArgs, vis: &mut T) { } MacArgs::Eq(eq_span, token) => { vis.visit_span(eq_span); - if vis.token_visiting_enabled() { + if T::VISIT_TOKENS { visit_token(token, vis); } else { // The value in `#[key = VALUE]` must be visited as an expression for backward @@ -682,7 +680,7 @@ pub fn visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) { // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. pub fn visit_tts<T: MutVisitor>(TokenStream(tts): &mut TokenStream, vis: &mut T) { - if vis.token_visiting_enabled() && !tts.is_empty() { + if T::VISIT_TOKENS && !tts.is_empty() { let tts = Lrc::make_mut(tts); visit_vec(tts, |(tree, _is_joint)| visit_tt(tree, vis)); } @@ -692,14 +690,14 @@ pub fn visit_attr_annotated_tts<T: MutVisitor>( AttrAnnotatedTokenStream(tts): &mut AttrAnnotatedTokenStream, vis: &mut T, ) { - if vis.token_visiting_enabled() && !tts.is_empty() { + if T::VISIT_TOKENS && !tts.is_empty() { let tts = Lrc::make_mut(tts); visit_vec(tts, |(tree, _is_joint)| visit_attr_annotated_tt(tree, vis)); } } pub fn visit_lazy_tts_opt_mut<T: MutVisitor>(lazy_tts: Option<&mut LazyTokenStream>, vis: &mut T) { - if vis.token_visiting_enabled() { + if T::VISIT_TOKENS { if let Some(lazy_tts) = lazy_tts { let mut tts = lazy_tts.create_token_stream(); visit_attr_annotated_tts(&mut tts, vis); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 22f93f50788..405e9035c4c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1332,15 +1332,11 @@ impl<'hir> LoweringContext<'_, 'hir> { body: &Block, opt_label: Option<Label>, ) -> hir::Expr<'hir> { - let orig_head_span = head.span; // expand <head> - let mut head = self.lower_expr_mut(head); - let desugared_span = self.mark_span_with_reason( - DesugaringKind::ForLoop(ForLoopLoc::Head), - orig_head_span, - None, - ); - head.span = self.lower_span(desugared_span); + let head = self.lower_expr_mut(head); + let desugared_span = + self.mark_span_with_reason(DesugaringKind::ForLoop(ForLoopLoc::Head), head.span, None); + let e_span = self.lower_span(e.span); let iter = Ident::with_dummy_span(sym::iter); @@ -1354,23 +1350,24 @@ impl<'hir> LoweringContext<'_, 'hir> { // `::std::option::Option::Some(val) => __next = val` let pat_arm = { let val_ident = Ident::with_dummy_span(sym::val); - let (val_pat, val_pat_hid) = self.pat_ident(pat.span, val_ident); - let val_expr = self.expr_ident(pat.span, val_ident, val_pat_hid); - let next_expr = self.expr_ident(pat.span, next_ident, next_pat_hid); + let pat_span = self.lower_span(pat.span); + let (val_pat, val_pat_hid) = self.pat_ident(pat_span, val_ident); + let val_expr = self.expr_ident(pat_span, val_ident, val_pat_hid); + let next_expr = self.expr_ident(pat_span, next_ident, next_pat_hid); let assign = self.arena.alloc(self.expr( - pat.span, - hir::ExprKind::Assign(next_expr, val_expr, self.lower_span(pat.span)), + pat_span, + hir::ExprKind::Assign(next_expr, val_expr, self.lower_span(pat_span)), ThinVec::new(), )); - let some_pat = self.pat_some(pat.span, val_pat); + let some_pat = self.pat_some(pat_span, val_pat); self.arm(some_pat, assign) }; // `::std::option::Option::None => break` let break_arm = { let break_expr = - self.with_loop_scope(e.id, |this| this.expr_break_alloc(e.span, ThinVec::new())); - let pat = self.pat_none(e.span); + self.with_loop_scope(e.id, |this| this.expr_break_alloc(e_span, ThinVec::new())); + let pat = self.pat_none(e_span); self.arm(pat, break_expr) }; @@ -1416,10 +1413,10 @@ impl<'hir> LoweringContext<'_, 'hir> { let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false)); let body_expr = self.expr_block(body_block, ThinVec::new()); - let body_stmt = self.stmt_expr(body.span, body_expr); + let body_stmt = self.stmt_expr(body_block.span, body_expr); let loop_block = self.block_all( - e.span, + e_span, arena_vec![self; next_let, match_stmt, pat_let, body_stmt], None, ); @@ -1429,7 +1426,7 @@ impl<'hir> LoweringContext<'_, 'hir> { loop_block, self.lower_label(opt_label), hir::LoopSource::ForLoop, - self.lower_span(e.span.with_hi(orig_head_span.hi())), + self.lower_span(e_span.with_hi(head.span.hi())), ); let loop_expr = self.arena.alloc(hir::Expr { hir_id: self.lower_node_id(e.id), @@ -1442,7 +1439,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let into_iter_span = self.mark_span_with_reason( DesugaringKind::ForLoop(ForLoopLoc::IntoIter), - orig_head_span, + head.span, None, ); @@ -1458,7 +1455,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // #82462: to correctly diagnose borrow errors, the block that contains // the iter expr needs to have a span that covers the loop body. let desugared_full_span = - self.mark_span_with_reason(DesugaringKind::ForLoop(ForLoopLoc::Head), e.span, None); + self.mark_span_with_reason(DesugaringKind::ForLoop(ForLoopLoc::Head), e_span, None); let match_expr = self.arena.alloc(self.expr_match( desugared_full_span, diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 37398894a20..439c728798d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -11,7 +11,6 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; -use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::sym; use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; @@ -247,6 +246,36 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { place_name, partially_str, loop_message ), ); + let sess = self.infcx.tcx.sess; + let ty = used_place.ty(self.body, self.infcx.tcx).ty; + // If we have a `&mut` ref, we need to reborrow. + if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { + // If we are in a loop this will be suggested later. + if !is_loop_move { + err.span_suggestion_verbose( + move_span.shrink_to_lo(), + &format!( + "consider creating a fresh reborrow of {} here", + self.describe_place(moved_place.as_ref()) + .map(|n| format!("`{}`", n)) + .unwrap_or_else( + || "the mutable reference".to_string() + ), + ), + "&mut *".to_string(), + Applicability::MachineApplicable, + ); + } + } else if let Ok(snippet) = + sess.source_map().span_to_snippet(move_span) + { + err.span_suggestion( + move_span, + "consider borrowing to avoid moving into the for loop", + format!("&{}", snippet), + Applicability::MaybeIncorrect, + ); + } } else { err.span_label( fn_call_span, @@ -315,35 +344,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { in_pattern = true; } } - - if let Some(DesugaringKind::ForLoop(_)) = move_span.desugaring_kind() { - let sess = self.infcx.tcx.sess; - let ty = used_place.ty(self.body, self.infcx.tcx).ty; - // If we have a `&mut` ref, we need to reborrow. - if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { - // If we are in a loop this will be suggested later. - if !is_loop_move { - err.span_suggestion_verbose( - move_span.shrink_to_lo(), - &format!( - "consider creating a fresh reborrow of {} here", - self.describe_place(moved_place.as_ref()) - .map(|n| format!("`{}`", n)) - .unwrap_or_else(|| "the mutable reference".to_string()), - ), - "&mut *".to_string(), - Applicability::MachineApplicable, - ); - } - } else if let Ok(snippet) = sess.source_map().span_to_snippet(move_span) { - err.span_suggestion( - move_span, - "consider borrowing to avoid moving into the for loop", - format!("&{}", snippet), - Applicability::MaybeIncorrect, - ); - } - } } use_spans.var_span_label_path_only( diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 855e6850b2e..692c20d7dfe 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -5,11 +5,10 @@ use rustc_middle::ty; use rustc_mir_dataflow::move_paths::{ IllegalMoveOrigin, IllegalMoveOriginKind, LookupResult, MoveError, MovePathIndex, }; -use rustc_span::source_map::DesugaringKind; use rustc_span::{sym, Span, DUMMY_SP}; use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; -use crate::diagnostics::UseSpans; +use crate::diagnostics::{FnSelfUseKind, UseSpans}; use crate::prefixes::PrefixSet; use crate::MirBorrowckCtxt; @@ -400,19 +399,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { | ty::Opaque(def_id, _) => def_id, _ => return err, }; - let is_option = self.infcx.tcx.is_diagnostic_item(sym::Option, def_id); - let is_result = self.infcx.tcx.is_diagnostic_item(sym::Result, def_id); - if (is_option || is_result) && use_spans.map_or(true, |v| !v.for_closure()) { + let diag_name = self.infcx.tcx.get_diagnostic_name(def_id); + if matches!(diag_name, Some(sym::Option | sym::Result)) + && use_spans.map_or(true, |v| !v.for_closure()) + { err.span_suggestion_verbose( span.shrink_to_hi(), - &format!( - "consider borrowing the `{}`'s content", - if is_option { "Option" } else { "Result" } - ), + &format!("consider borrowing the `{}`'s content", diag_name.unwrap()), ".as_ref()".to_string(), Applicability::MaybeIncorrect, ); - } else if matches!(span.desugaring_kind(), Some(DesugaringKind::ForLoop(_))) { + } else if let Some(UseSpans::FnSelfUse { + kind: FnSelfUseKind::Normal { implicit_into_iter: true, .. }, + .. + }) = use_spans + { let suggest = match self.infcx.tcx.get_diagnostic_item(sym::IntoIterator) { Some(def_id) => self.infcx.tcx.infer_ctxt().enter(|infcx| { type_known_to_meet_bound_modulo_regions( diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 32cc50eebe4..0a8d6122aa7 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -224,7 +224,7 @@ pub(crate) fn run_aot( tcx, (backend_config.clone(), cgu.name()), module_codegen, - rustc_middle::dep_graph::hash_result, + Some(rustc_middle::dep_graph::hash_result), ); if let Some((id, product)) = work_product { diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 9f96096574f..a3b8d328388 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -59,7 +59,13 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (Modul let start_time = Instant::now(); let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx); - let (module, _) = tcx.dep_graph.with_task(dep_node, tcx, cgu_name, module_codegen, dep_graph::hash_result); + let (module, _) = tcx.dep_graph.with_task( + dep_node, + tcx, + cgu_name, + module_codegen, + Some(dep_graph::hash_result), + ); let time_to_codegen = start_time.elapsed(); drop(prof_timer); diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index a6a553b31a3..5f3f5334475 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -11,7 +11,7 @@ doctest = false bitflags = "1.0" cstr = "0.2" libc = "0.2" -measureme = "9.1.0" +measureme = "10.0.0" snap = "1" tracing = "0.1" rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 3026c2fa030..8766caef6e3 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -113,8 +113,13 @@ pub fn compile_codegen_unit( let start_time = Instant::now(); let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx); - let (module, _) = - tcx.dep_graph.with_task(dep_node, tcx, cgu_name, module_codegen, dep_graph::hash_result); + let (module, _) = tcx.dep_graph.with_task( + dep_node, + tcx, + cgu_name, + module_codegen, + Some(dep_graph::hash_result), + ); let time_to_codegen = start_time.elapsed(); // We assume that the cost to run LLVM on a CGU is proportional to diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 609316ea69f..accb54e4645 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -480,14 +480,11 @@ pub fn compute_debuginfo_vtable_name<'tcx>( } if let Some(trait_ref) = trait_ref { - push_item_name(tcx, trait_ref.skip_binder().def_id, true, &mut vtable_name); + let trait_ref = + tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref); + push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name); visited.clear(); - push_generic_params_internal( - tcx, - trait_ref.skip_binder().substs, - &mut vtable_name, - &mut visited, - ); + push_generic_params_internal(tcx, trait_ref.substs, &mut vtable_name, &mut visited); } else { vtable_name.push_str("_"); } diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index df4cc295fac..80551518d3c 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -1,6 +1,5 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; @@ -44,8 +43,8 @@ fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } else { false } - } else if let Some(fn_like) = FnLikeNode::from_node(node) { - if fn_like.constness() == hir::Constness::Const { + } else if let Some(fn_kind) = node.fn_kind() { + if fn_kind.constness() == hir::Constness::Const { return true; } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 202c9cad8eb..8efe3eb868b 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -72,9 +72,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { let span = self.find_closest_untracked_caller_location(); let (file, line, col) = self.location_triple_for_span(span); return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); - } else if Some(def_id) == self.tcx.lang_items().panic_fmt() - || Some(def_id) == self.tcx.lang_items().begin_panic_fmt() - { + } else if Some(def_id) == self.tcx.lang_items().panic_fmt() { // For panic_fmt, call const_panic_fmt instead. if let Some(const_panic_fmt) = self.tcx.lang_items().const_panic_fmt() { return Ok(Some( diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs index 0a852282f8f..58d0f1a3ad8 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs @@ -79,7 +79,6 @@ pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { || Some(def_id) == tcx.lang_items().panic_display() || Some(def_id) == tcx.lang_items().begin_panic_fn() || Some(def_id) == tcx.lang_items().panic_fmt() - || Some(def_id) == tcx.lang_items().begin_panic_fmt() } /// Returns `true` if this `DefId` points to one of the lang items that will be handled differently diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 49962570129..e3395df3590 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -23,7 +23,7 @@ rustc-hash = "1.1.0" smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } rustc_index = { path = "../rustc_index", package = "rustc_index" } bitflags = "1.2.1" -measureme = "9.1.0" +measureme = "10.0.0" libc = "0.2" stacker = "0.1.14" tempfile = "3.2" diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 0bbd0eda0c6..c21939209fc 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -110,12 +110,14 @@ bitflags::bitflags! { const FUNCTION_ARGS = 1 << 6; const LLVM = 1 << 7; const INCR_RESULT_HASHING = 1 << 8; + const ARTIFACT_SIZES = 1 << 9; const DEFAULT = Self::GENERIC_ACTIVITIES.bits | Self::QUERY_PROVIDERS.bits | Self::QUERY_BLOCKED.bits | Self::INCR_CACHE_LOADS.bits | - Self::INCR_RESULT_HASHING.bits; + Self::INCR_RESULT_HASHING.bits | + Self::ARTIFACT_SIZES.bits; const ARGS = Self::QUERY_KEYS.bits | Self::FUNCTION_ARGS.bits; } @@ -136,6 +138,7 @@ const EVENT_FILTERS_BY_NAME: &[(&str, EventFilter)] = &[ ("args", EventFilter::ARGS), ("llvm", EventFilter::LLVM), ("incr-result-hashing", EventFilter::INCR_RESULT_HASHING), + ("artifact-sizes", EventFilter::ARTIFACT_SIZES), ]; /// Something that uniquely identifies a query invocation. @@ -285,6 +288,33 @@ impl SelfProfilerRef { }) } + /// Record the size of an artifact that the compiler produces + /// + /// `artifact_kind` is the class of artifact (e.g., query_cache, object_file, etc.) + /// `artifact_name` is an identifier to the specific artifact being stored (usually a filename) + #[inline(always)] + pub fn artifact_size<A>(&self, artifact_kind: &str, artifact_name: A, size: u64) + where + A: Borrow<str> + Into<String>, + { + drop(self.exec(EventFilter::ARTIFACT_SIZES, |profiler| { + let builder = EventIdBuilder::new(&profiler.profiler); + let event_label = profiler.get_or_alloc_cached_string(artifact_kind); + let event_arg = profiler.get_or_alloc_cached_string(artifact_name); + let event_id = builder.from_label_and_arg(event_label, event_arg); + let thread_id = get_thread_id(); + + profiler.profiler.record_integer_event( + profiler.artifact_size_event_kind, + event_id, + thread_id, + size, + ); + + TimingGuard::none() + })) + } + #[inline(always)] pub fn generic_activity_with_args( &self, @@ -372,7 +402,7 @@ impl SelfProfilerRef { ) { drop(self.exec(event_filter, |profiler| { let event_id = StringId::new_virtual(query_invocation_id.0); - let thread_id = std::thread::current().id().as_u64().get() as u32; + let thread_id = get_thread_id(); profiler.profiler.record_instant_event( event_kind(profiler), @@ -425,6 +455,7 @@ pub struct SelfProfiler { incremental_result_hashing_event_kind: StringId, query_blocked_event_kind: StringId, query_cache_hit_event_kind: StringId, + artifact_size_event_kind: StringId, } impl SelfProfiler { @@ -447,6 +478,7 @@ impl SelfProfiler { profiler.alloc_string("IncrementalResultHashing"); let query_blocked_event_kind = profiler.alloc_string("QueryBlocked"); let query_cache_hit_event_kind = profiler.alloc_string("QueryCacheHit"); + let artifact_size_event_kind = profiler.alloc_string("ArtifactSize"); let mut event_filter_mask = EventFilter::empty(); @@ -491,6 +523,7 @@ impl SelfProfiler { incremental_result_hashing_event_kind, query_blocked_event_kind, query_cache_hit_event_kind, + artifact_size_event_kind, }) } @@ -561,7 +594,7 @@ impl<'a> TimingGuard<'a> { event_kind: StringId, event_id: EventId, ) -> TimingGuard<'a> { - let thread_id = std::thread::current().id().as_u64().get() as u32; + let thread_id = get_thread_id(); let raw_profiler = &profiler.profiler; let timing_guard = raw_profiler.start_recording_interval_event(event_kind, event_id, thread_id); @@ -655,6 +688,10 @@ pub fn duration_to_secs_str(dur: std::time::Duration) -> String { format!("{:.3}", dur.as_secs_f64()) } +fn get_thread_id() -> u32 { + std::thread::current().id().as_u64().get() as u32 +} + // Memory reporting cfg_if! { if #[cfg(windows)] { diff --git a/compiler/rustc_error_codes/src/error_codes/E0637.md b/compiler/rustc_error_codes/src/error_codes/E0637.md index d9068950bdf..62d5565df27 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0637.md +++ b/compiler/rustc_error_codes/src/error_codes/E0637.md @@ -1,35 +1,51 @@ -An underscore `_` character has been used as the identifier for a lifetime. +`'_` lifetime name or `&T` without an explicit lifetime name has been used +on illegal place. Erroneous code example: ```compile_fail,E0106,E0637 -fn longest<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { - //^^ `'_` is a reserved lifetime name +fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { + //^^ `'_` is a reserved lifetime name if str1.len() > str2.len() { str1 } else { str2 } } + +fn and_without_explicit_lifetime<T>() +where + T: Into<&u32>, + //^ `&` without an explicit lifetime name +{ +} ``` -`'_`, cannot be used as a lifetime identifier because it is a reserved for the -anonymous lifetime. To fix this, use a lowercase letter such as 'a, or a series -of lowercase letters such as `'foo`. For more information, see [the -book][bk-no]. For more information on using the anonymous lifetime in rust -nightly, see [the nightly book][bk-al]. +First, `'_` cannot be used as a lifetime identifier in some places +because it is a reserved for the anonymous lifetime. Second, `&T` +without an explicit lifetime name cannot also be used in some places. +To fix them, use a lowercase letter such as `'a`, or a series +of lowercase letters such as `'foo`. For more information about lifetime +identifier, see [the book][bk-no]. For more information on using +the anonymous lifetime in Rust 2018, see [the Rust 2018 blog post][blog-al]. Corrected example: ``` -fn longest<'a>(str1: &'a str, str2: &'a str) -> &'a str { +fn underscore_lifetime<'a>(str1: &'a str, str2: &'a str) -> &'a str { if str1.len() > str2.len() { str1 } else { str2 } } + +fn and_without_explicit_lifetime<'foo, T>() +where + T: Into<&'foo u32>, +{ +} ``` [bk-no]: https://doc.rust-lang.org/book/appendix-02-operators.html#non-operator-symbols -[bk-al]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/ownership-and-lifetimes/the-anonymous-lifetime.html +[blog-al]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html#more-lifetime-elision-rules diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 4663dd80fa8..88e1623012b 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -19,9 +19,7 @@ use std::mem; struct Marker(LocalExpnId, Transparency); impl MutVisitor for Marker { - fn token_visiting_enabled(&self) -> bool { - true - } + const VISIT_TOKENS: bool = true; fn visit_span(&mut self, span: &mut Span) { *span = span.apply_mark(self.0.to_expn_id(), self.1) diff --git a/compiler/rustc_expand/src/mut_visit/tests.rs b/compiler/rustc_expand/src/mut_visit/tests.rs index 0068539fb3b..8974d45b4d8 100644 --- a/compiler/rustc_expand/src/mut_visit/tests.rs +++ b/compiler/rustc_expand/src/mut_visit/tests.rs @@ -15,9 +15,8 @@ fn print_crate_items(krate: &ast::Crate) -> String { struct ToZzIdentMutVisitor; impl MutVisitor for ToZzIdentMutVisitor { - fn token_visiting_enabled(&self) -> bool { - true - } + const VISIT_TOKENS: bool = true; + fn visit_ident(&mut self, ident: &mut Ident) { *ident = Ident::from_str("zz"); } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index b5c1e31c258..6f25715fbec 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,6 +1,7 @@ use crate::def::{CtorKind, DefKind, Res}; use crate::def_id::DefId; crate use crate::hir_id::{HirId, ItemLocalId}; +use crate::intravisit::FnKind; use crate::LangItem; use rustc_ast::util::parser::ExprPrecedence; @@ -3258,6 +3259,32 @@ impl<'hir> Node<'hir> { _ => None, } } + + pub fn fn_kind(self) -> Option<FnKind<'hir>> { + match self { + Node::Item(i) => match i.kind { + ItemKind::Fn(ref sig, ref generics, _) => { + Some(FnKind::ItemFn(i.ident, generics, sig.header, &i.vis)) + } + _ => None, + }, + Node::TraitItem(ti) => match ti.kind { + TraitItemKind::Fn(ref sig, TraitFn::Provided(_)) => { + Some(FnKind::Method(ti.ident, sig, None)) + } + _ => None, + }, + Node::ImplItem(ii) => match ii.kind { + ImplItemKind::Fn(ref sig, _) => Some(FnKind::Method(ii.ident, sig, Some(&ii.vis))), + _ => None, + }, + Node::Expr(e) => match e.kind { + ExprKind::Closure(..) => Some(FnKind::Closure), + _ => None, + }, + _ => None, + } + } } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 3e58af1f167..cff543760f4 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -117,6 +117,14 @@ impl<'a> FnKind<'a> { FnKind::Closure => None, } } + + pub fn constness(self) -> Constness { + self.header().map_or(Constness::NotConst, |header| header.constness) + } + + pub fn asyncness(self) -> IsAsync { + self.header().map_or(IsAsync::NotAsync, |header| header.asyncness) + } } /// An abstract representation of the HIR `rustc_middle::hir::map::Map`. diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index f35353dbfb5..97d4123138e 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -292,7 +292,6 @@ language_item_table! { PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None; /// libstd panic entry point. Necessary for const eval to be able to catch it BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; - BeginPanicFmt, sym::begin_panic_fmt, begin_panic_fmt, Target::Fn, GenericRequirement::None; ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1); diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index d42e2f7a99c..571337a8dcb 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -126,30 +126,36 @@ impl IfThisChanged<'tcx> { if attr.has_name(sym::rustc_if_this_changed) { let dep_node_interned = self.argument(attr); let dep_node = match dep_node_interned { - None => DepNode::from_def_path_hash(def_path_hash, DepKind::hir_owner), - Some(n) => match DepNode::from_label_string(&n.as_str(), def_path_hash) { - Ok(n) => n, - Err(()) => { - self.tcx.sess.span_fatal( - attr.span, - &format!("unrecognized DepNode variant {:?}", n), - ); + None => { + DepNode::from_def_path_hash(self.tcx, def_path_hash, DepKind::hir_owner) + } + Some(n) => { + match DepNode::from_label_string(self.tcx, &n.as_str(), def_path_hash) { + Ok(n) => n, + Err(()) => { + self.tcx.sess.span_fatal( + attr.span, + &format!("unrecognized DepNode variant {:?}", n), + ); + } } - }, + } }; self.if_this_changed.push((attr.span, def_id.to_def_id(), dep_node)); } else if attr.has_name(sym::rustc_then_this_would_need) { let dep_node_interned = self.argument(attr); let dep_node = match dep_node_interned { - Some(n) => match DepNode::from_label_string(&n.as_str(), def_path_hash) { - Ok(n) => n, - Err(()) => { - self.tcx.sess.span_fatal( - attr.span, - &format!("unrecognized DepNode variant {:?}", n), - ); + Some(n) => { + match DepNode::from_label_string(self.tcx, &n.as_str(), def_path_hash) { + Ok(n) => n, + Err(()) => { + self.tcx.sess.span_fatal( + attr.span, + &format!("unrecognized DepNode variant {:?}", n), + ); + } } - }, + } None => { self.tcx.sess.span_fatal(attr.span, "missing DepNode variant"); } diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 55286384de3..b2eaf61b7d1 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -15,7 +15,7 @@ use rustc_ast::{self as ast, Attribute, NestedMetaItem}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::Node as HirNode; @@ -302,18 +302,6 @@ impl DirtyCleanVisitor<'tcx> { out } - fn dep_nodes<'l>( - &self, - labels: &'l Labels, - def_id: DefId, - ) -> impl Iterator<Item = DepNode> + 'l { - let def_path_hash = self.tcx.def_path_hash(def_id); - labels.iter().map(move |label| match DepNode::from_label_string(label, def_path_hash) { - Ok(dep_node) => dep_node, - Err(()) => unreachable!("label: {}", label), - }) - } - fn dep_node_str(&self, dep_node: &DepNode) -> String { if let Some(def_id) = dep_node.extract_def_id(self.tcx) { format!("{:?}({})", dep_node.kind, self.tcx.def_path_str(def_id)) @@ -345,16 +333,19 @@ impl DirtyCleanVisitor<'tcx> { } fn check_item(&mut self, item_id: LocalDefId, item_span: Span) { + let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id()); for attr in self.tcx.get_attrs(item_id.to_def_id()).iter() { let assertion = match self.assertion_maybe(item_id, attr) { Some(a) => a, None => continue, }; self.checked_attrs.insert(attr.id); - for dep_node in self.dep_nodes(&assertion.clean, item_id.to_def_id()) { + for label in assertion.clean { + let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_clean(item_span, dep_node); } - for dep_node in self.dep_nodes(&assertion.dirty, item_id.to_def_id()) { + for label in assertion.dirty { + let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_dirty(item_span, dep_node); } } diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs index 572a4fc6971..392c5bdc15a 100644 --- a/compiler/rustc_incremental/src/persist/file_format.rs +++ b/compiler/rustc_incremental/src/persist/file_format.rs @@ -95,6 +95,12 @@ where return; } + sess.prof.artifact_size( + &name.replace(' ', "_"), + path_buf.file_name().unwrap().to_string_lossy(), + encoder.position() as u64, + ); + debug!("save: data written to disk successfully"); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index 8dcdd4b149e..90bc5b3b2fe 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -143,9 +143,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // similar to the asyncness fn in rustc_ty_utils::ty let hir_id = self.tcx().hir().local_def_id_to_hir_id(local_def_id); let node = self.tcx().hir().get(hir_id); - let fn_like = rustc_middle::hir::map::blocks::FnLikeNode::from_node(node)?; - - Some(fn_like.asyncness()) + let fn_kind = node.fn_kind()?; + Some(fn_kind.asyncness()) } // Here, we check for the case where the anonymous region diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index bcfa0ef3520..eea32083568 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -838,6 +838,7 @@ pub fn create_global_ctxt<'tcx>( dep_graph, queries.on_disk_cache.as_ref().map(OnDiskCache::as_dyn), queries.as_dyn(), + rustc_query_impl::query_callbacks(arena), crate_name, outputs, ) diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index d147148ac71..d8883b0e66d 100644 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs @@ -134,9 +134,8 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { Applicability::MachineApplicable, ); if self.for_expr_span == expr.span { - let expr_span = expr.span.ctxt().outer_expn_data().call_site; diag.span_suggestion( - receiver_arg.span.shrink_to_hi().to(expr_span.shrink_to_hi()), + receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), "or remove `.into_iter()` to iterate by value", String::new(), Applicability::MaybeIncorrect, diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index c54ea610602..bbd30c9327a 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -529,6 +529,15 @@ impl<'a> CrateLocator<'a> { let mut err_data: Option<Vec<PathBuf>> = None; for (lib, kind) in m { info!("{} reading metadata from: {}", flavor, lib.display()); + if flavor == CrateFlavor::Rmeta && lib.metadata().map_or(false, |m| m.len() == 0) { + // Empty files will cause get_metadata_section to fail. Rmeta + // files can be empty, for example with binaries (which can + // often appear with `cargo check` when checking a library as + // a unittest). We don't want to emit a user-visible warning + // in this case as it is not a real problem. + debug!("skipping empty file"); + continue; + } let (hash, metadata) = match get_metadata_section(self.target, flavor, &lib, self.metadata_loader) { Ok(blob) => { diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 4a027cb7ebe..420c500a7de 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -100,6 +100,8 @@ macro_rules! arena_types { // This is used to decode the &'tcx [Span] for InlineAsm's line_spans. [decode] span: rustc_span::Span, [decode] used_trait_imports: rustc_data_structures::fx::FxHashSet<rustc_hir::def_id::LocalDefId>, + + [] dep_kind: rustc_middle::dep_graph::DepKindStruct, ], $tcx); ) } diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 23d475a5953..f3100010770 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -75,145 +75,71 @@ pub use rustc_query_system::dep_graph::{DepContext, DepNodeParams}; /// of the `DepKind`. Overall, this allows to implement `DepContext` using this manual /// jump table instead of large matches. pub struct DepKindStruct { - /// Whether the DepNode has parameters (query keys). - pub(super) has_params: bool, - /// Anonymous queries cannot be replayed from one compiler invocation to the next. /// When their result is needed, it is recomputed. They are useful for fine-grained /// dependency tracking, and caching within one compiler invocation. - pub(super) is_anon: bool, + pub is_anon: bool, /// Eval-always queries do not track their dependencies, and are always recomputed, even if /// their inputs have not changed since the last compiler invocation. The result is still /// cached within one compiler invocation. - pub(super) is_eval_always: bool, + pub is_eval_always: bool, /// Whether the query key can be recovered from the hashed fingerprint. /// See [DepNodeParams] trait for the behaviour of each key type. - // FIXME: Make this a simple boolean once DepNodeParams::fingerprint_style - // can be made a specialized associated const. - fingerprint_style: fn() -> FingerprintStyle, -} - -impl std::ops::Deref for DepKind { - type Target = DepKindStruct; - fn deref(&self) -> &DepKindStruct { - &DEP_KINDS[*self as usize] - } + pub fingerprint_style: FingerprintStyle, + + /// The red/green evaluation system will try to mark a specific DepNode in the + /// dependency graph as green by recursively trying to mark the dependencies of + /// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` + /// where we don't know if it is red or green and we therefore actually have + /// to recompute its value in order to find out. Since the only piece of + /// information that we have at that point is the `DepNode` we are trying to + /// re-evaluate, we need some way to re-run a query from just that. This is what + /// `force_from_dep_node()` implements. + /// + /// In the general case, a `DepNode` consists of a `DepKind` and an opaque + /// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint + /// is usually constructed by computing a stable hash of the query-key that the + /// `DepNode` corresponds to. Consequently, it is not in general possible to go + /// back from hash to query-key (since hash functions are not reversible). For + /// this reason `force_from_dep_node()` is expected to fail from time to time + /// because we just cannot find out, from the `DepNode` alone, what the + /// corresponding query-key is and therefore cannot re-run the query. + /// + /// The system deals with this case letting `try_mark_green` fail which forces + /// the root query to be re-evaluated. + /// + /// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. + /// Fortunately, we can use some contextual information that will allow us to + /// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we + /// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a + /// valid `DefPathHash`. Since we also always build a huge table that maps every + /// `DefPathHash` in the current codebase to the corresponding `DefId`, we have + /// everything we need to re-run the query. + /// + /// Take the `mir_promoted` query as an example. Like many other queries, it + /// just has a single parameter: the `DefId` of the item it will compute the + /// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` + /// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` + /// is actually a `DefPathHash`, and can therefore just look up the corresponding + /// `DefId` in `tcx.def_path_hash_to_def_id`. + pub force_from_dep_node: Option<fn(tcx: TyCtxt<'_>, dep_node: DepNode) -> bool>, + + /// Invoke a query to put the on-disk cached value in memory. + pub try_load_from_on_disk_cache: Option<fn(TyCtxt<'_>, DepNode)>, } impl DepKind { #[inline(always)] - pub fn fingerprint_style(&self) -> FingerprintStyle { + pub fn fingerprint_style(self, tcx: TyCtxt<'_>) -> FingerprintStyle { // Only fetch the DepKindStruct once. - let data: &DepKindStruct = &**self; + let data = tcx.query_kind(self); if data.is_anon { return FingerprintStyle::Opaque; } - - (data.fingerprint_style)() - } -} - -// erase!() just makes tokens go away. It's used to specify which macro argument -// is repeated (i.e., which sub-expression of the macro we are in) but don't need -// to actually use any of the arguments. -macro_rules! erase { - ($x:tt) => {{}}; -} - -macro_rules! is_anon_attr { - (anon) => { - true - }; - ($attr:ident) => { - false - }; -} - -macro_rules! is_eval_always_attr { - (eval_always) => { - true - }; - ($attr:ident) => { - false - }; -} - -macro_rules! contains_anon_attr { - ($(($attr:ident $($attr_args:tt)* )),*) => ({$(is_anon_attr!($attr) | )* false}); -} - -macro_rules! contains_eval_always_attr { - ($(($attr:ident $($attr_args:tt)* )),*) => ({$(is_eval_always_attr!($attr) | )* false}); -} - -#[allow(non_upper_case_globals)] -pub mod dep_kind { - use super::*; - use crate::ty::query::query_keys; - use rustc_query_system::dep_graph::FingerprintStyle; - - // We use this for most things when incr. comp. is turned off. - pub const Null: DepKindStruct = DepKindStruct { - has_params: false, - is_anon: false, - is_eval_always: false, - - fingerprint_style: || FingerprintStyle::Unit, - }; - - pub const TraitSelect: DepKindStruct = DepKindStruct { - has_params: false, - is_anon: true, - is_eval_always: false, - - fingerprint_style: || FingerprintStyle::Unit, - }; - - pub const CompileCodegenUnit: DepKindStruct = DepKindStruct { - has_params: true, - is_anon: false, - is_eval_always: false, - - fingerprint_style: || FingerprintStyle::Opaque, - }; - - pub const CompileMonoItem: DepKindStruct = DepKindStruct { - has_params: true, - is_anon: false, - is_eval_always: false, - - fingerprint_style: || FingerprintStyle::Opaque, - }; - - macro_rules! define_query_dep_kinds { - ($( - [$($attrs:tt)*] - $variant:ident $(( $tuple_arg_ty:ty $(,)? ))* - ,)*) => ( - $(pub const $variant: DepKindStruct = { - const has_params: bool = $({ erase!($tuple_arg_ty); true } |)* false; - const is_anon: bool = contains_anon_attr!($($attrs)*); - const is_eval_always: bool = contains_eval_always_attr!($($attrs)*); - - #[inline(always)] - fn fingerprint_style() -> rustc_query_system::dep_graph::FingerprintStyle { - <query_keys::$variant<'_> as DepNodeParams<TyCtxt<'_>>> - ::fingerprint_style() - } - - DepKindStruct { - has_params, - is_anon, - is_eval_always, - fingerprint_style, - } - };)* - ); + data.fingerprint_style } - - rustc_dep_node_append!([define_query_dep_kinds!][]); } macro_rules! define_dep_nodes { @@ -225,12 +151,10 @@ macro_rules! define_dep_nodes { ) => ( #[macro_export] macro_rules! make_dep_kind_array { - ($mod:ident) => {[ $(($mod::$variant),)* ]}; + ($mod:ident) => {[ $($mod::$variant()),* ]}; } - static DEP_KINDS: &[DepKindStruct] = &make_dep_kind_array!(dep_kind); - - /// This enum serves as an index into the `DEP_KINDS` array. + /// This enum serves as an index into arrays built by `make_dep_kind_array`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] #[allow(non_camel_case_types)] pub enum DepKind { @@ -296,7 +220,7 @@ pub trait DepNodeExt: Sized { /// Construct a DepNode from the given DepKind and DefPathHash. This /// method will assert that the given DepKind actually requires a /// single DefId/DefPathHash parameter. - fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> Self; + fn from_def_path_hash(tcx: TyCtxt<'_>, def_path_hash: DefPathHash, kind: DepKind) -> Self; /// Extracts the DefId corresponding to this DepNode. This will work /// if two conditions are met: @@ -311,7 +235,11 @@ pub trait DepNodeExt: Sized { fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option<DefId>; /// Used in testing - fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result<Self, ()>; + fn from_label_string( + tcx: TyCtxt<'_>, + label: &str, + def_path_hash: DefPathHash, + ) -> Result<Self, ()>; /// Used in testing fn has_label_string(label: &str) -> bool; @@ -321,8 +249,8 @@ impl DepNodeExt for DepNode { /// Construct a DepNode from the given DepKind and DefPathHash. This /// method will assert that the given DepKind actually requires a /// single DefId/DefPathHash parameter. - fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> DepNode { - debug_assert!(kind.fingerprint_style() == FingerprintStyle::DefPathHash); + fn from_def_path_hash(tcx: TyCtxt<'_>, def_path_hash: DefPathHash, kind: DepKind) -> DepNode { + debug_assert!(kind.fingerprint_style(tcx) == FingerprintStyle::DefPathHash); DepNode { kind, hash: def_path_hash.0.into() } } @@ -337,31 +265,27 @@ impl DepNodeExt for DepNode { /// refers to something from the previous compilation session that /// has been removed. fn extract_def_id(&self, tcx: TyCtxt<'tcx>) -> Option<DefId> { - if self.kind.fingerprint_style() == FingerprintStyle::DefPathHash { - Some( - tcx.on_disk_cache - .as_ref()? - .def_path_hash_to_def_id(tcx, DefPathHash(self.hash.into())), - ) + if self.kind.fingerprint_style(tcx) == FingerprintStyle::DefPathHash { + Some(tcx.def_path_hash_to_def_id(DefPathHash(self.hash.into()))) } else { None } } /// Used in testing - fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result<DepNode, ()> { + fn from_label_string( + tcx: TyCtxt<'_>, + label: &str, + def_path_hash: DefPathHash, + ) -> Result<DepNode, ()> { let kind = dep_kind_from_label_string(label)?; - match kind.fingerprint_style() { + match kind.fingerprint_style(tcx) { FingerprintStyle::Opaque => Err(()), - FingerprintStyle::Unit => { - if !kind.has_params { - Ok(DepNode::new_no_params(kind)) - } else { - Err(()) - } + FingerprintStyle::Unit => Ok(DepNode::new_no_params(tcx, kind)), + FingerprintStyle::DefPathHash => { + Ok(DepNode::from_def_path_hash(tcx, def_path_hash, kind)) } - FingerprintStyle::DefPathHash => Ok(DepNode::from_def_path_hash(def_path_hash, kind)), } } @@ -377,10 +301,12 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for () { FingerprintStyle::Unit } + #[inline(always)] fn to_fingerprint(&self, _: TyCtxt<'tcx>) -> Fingerprint { Fingerprint::ZERO } + #[inline(always)] fn recover(_: TyCtxt<'tcx>, _: &DepNode) -> Option<Self> { Some(()) } @@ -392,14 +318,17 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for DefId { FingerprintStyle::DefPathHash } + #[inline(always)] fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { tcx.def_path_hash(*self).0 } + #[inline(always)] fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { tcx.def_path_str(*self) } + #[inline(always)] fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> { dep_node.extract_def_id(tcx) } @@ -411,14 +340,17 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for LocalDefId { FingerprintStyle::DefPathHash } + #[inline(always)] fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { self.to_def_id().to_fingerprint(tcx) } + #[inline(always)] fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { self.to_def_id().to_debug_str(tcx) } + #[inline(always)] fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> { dep_node.extract_def_id(tcx).map(|id| id.expect_local()) } @@ -430,15 +362,18 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for CrateNum { FingerprintStyle::DefPathHash } + #[inline(always)] fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { let def_id = DefId { krate: *self, index: CRATE_DEF_INDEX }; def_id.to_fingerprint(tcx) } + #[inline(always)] fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { tcx.crate_name(*self).to_string() } + #[inline(always)] fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> { dep_node.extract_def_id(tcx).map(|id| id.krate) } @@ -453,6 +388,7 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for (DefId, DefId) { // We actually would not need to specialize the implementation of this // method but it's faster to combine the hashes than to instantiate a full // hashing context and stable-hashing state. + #[inline(always)] fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { let (def_id_0, def_id_1) = *self; @@ -462,6 +398,7 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for (DefId, DefId) { def_path_hash_0.0.combine(def_path_hash_1.0) } + #[inline(always)] fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { let (def_id_0, def_id_1) = *self; @@ -478,6 +415,7 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for HirId { // We actually would not need to specialize the implementation of this // method but it's faster to combine the hashes than to instantiate a full // hashing context and stable-hashing state. + #[inline(always)] fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { let HirId { owner, local_id } = *self; diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index cda99639074..79d7ca32f35 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -12,7 +12,7 @@ pub use rustc_query_system::dep_graph::{ SerializedDepNodeIndex, WorkProduct, WorkProductId, }; -pub use dep_node::{label_strs, DepKind, DepNode, DepNodeExt}; +pub use dep_node::{label_strs, DepKind, DepKindStruct, DepNode, DepNodeExt}; crate use dep_node::{make_compile_codegen_unit, make_compile_mono_item}; pub type DepGraph = rustc_query_system::dep_graph::DepGraph<DepKind>; @@ -24,29 +24,8 @@ pub type EdgeFilter = rustc_query_system::dep_graph::debug::EdgeFilter<DepKind>; impl rustc_query_system::dep_graph::DepKind for DepKind { const NULL: Self = DepKind::Null; - #[inline(always)] - fn fingerprint_style(&self) -> rustc_query_system::dep_graph::FingerprintStyle { - DepKind::fingerprint_style(self) - } - - #[inline(always)] - fn is_eval_always(&self) -> bool { - self.is_eval_always - } - - #[inline(always)] - fn has_params(&self) -> bool { - self.has_params - } - fn debug_node(node: &DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", node.kind)?; - - if !node.kind.has_params && !node.kind.is_anon { - return Ok(()); - } - - write!(f, "(")?; + write!(f, "{:?}(", node.kind)?; ty::tls::with_opt(|opt_tcx| { if let Some(tcx) = opt_tcx { @@ -110,4 +89,51 @@ impl<'tcx> DepContext for TyCtxt<'tcx> { fn sess(&self) -> &Session { self.sess } + + #[inline(always)] + fn fingerprint_style(&self, kind: DepKind) -> rustc_query_system::dep_graph::FingerprintStyle { + kind.fingerprint_style(*self) + } + + #[inline(always)] + fn is_eval_always(&self, kind: DepKind) -> bool { + self.query_kind(kind).is_eval_always + } + + fn try_force_from_dep_node(&self, dep_node: DepNode) -> bool { + debug!("try_force_from_dep_node({:?}) --- trying to force", dep_node); + + // We must avoid ever having to call `force_from_dep_node()` for a + // `DepNode::codegen_unit`: + // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we + // would always end up having to evaluate the first caller of the + // `codegen_unit` query that *is* reconstructible. This might very well be + // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just + // to re-trigger calling the `codegen_unit` query with the right key. At + // that point we would already have re-done all the work we are trying to + // avoid doing in the first place. + // The solution is simple: Just explicitly call the `codegen_unit` query for + // each CGU, right after partitioning. This way `try_mark_green` will always + // hit the cache instead of having to go through `force_from_dep_node`. + // This assertion makes sure, we actually keep applying the solution above. + debug_assert!( + dep_node.kind != DepKind::codegen_unit, + "calling force_from_dep_node() on DepKind::codegen_unit" + ); + + let cb = self.query_kind(dep_node.kind); + if let Some(f) = cb.force_from_dep_node { + f(*self, dep_node); + true + } else { + false + } + } + + fn try_load_from_on_disk_cache(&self, dep_node: DepNode) { + let cb = self.query_kind(dep_node.kind); + if let Some(f) = cb.try_load_from_on_disk_cache { + f(*self, dep_node) + } + } } diff --git a/compiler/rustc_middle/src/hir/map/blocks.rs b/compiler/rustc_middle/src/hir/map/blocks.rs deleted file mode 100644 index 8efec8ef567..00000000000 --- a/compiler/rustc_middle/src/hir/map/blocks.rs +++ /dev/null @@ -1,239 +0,0 @@ -//! This module provides a simplified abstraction for working with -//! code blocks identified by their integer `NodeId`. In particular, -//! it captures a common set of attributes that all "function-like -//! things" (represented by `FnLike` instances) share. For example, -//! all `FnLike` instances have a type signature (be it explicit or -//! inferred). And all `FnLike` instances have a body, i.e., the code -//! that is run when the function-like thing it represents is invoked. -//! -//! With the above abstraction in place, one can treat the program -//! text as a collection of blocks of code (and most such blocks are -//! nested within a uniquely determined `FnLike`), and users can ask -//! for the `Code` associated with a particular NodeId. - -use crate::hir::map::Map; -use rustc_hir as hir; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{Expr, FnDecl, Node}; -use rustc_span::symbol::Ident; -use rustc_span::Span; - -/// An FnLikeNode is a Node that is like a fn, in that it has a decl -/// and a body (as well as a NodeId, a span, etc). -/// -/// More specifically, it is one of either: -/// -/// - A function item, -/// - A closure expr (i.e., an ExprKind::Closure), or -/// - The default implementation for a trait method. -/// -/// To construct one, use the `Code::from_node` function. -#[derive(Copy, Clone, Debug)] -pub struct FnLikeNode<'a> { - node: Node<'a>, -} - -/// MaybeFnLike wraps a method that indicates if an object -/// corresponds to some FnLikeNode. -trait MaybeFnLike { - fn is_fn_like(&self) -> bool; -} - -impl MaybeFnLike for hir::Item<'_> { - fn is_fn_like(&self) -> bool { - matches!(self.kind, hir::ItemKind::Fn(..)) - } -} - -impl MaybeFnLike for hir::ImplItem<'_> { - fn is_fn_like(&self) -> bool { - matches!(self.kind, hir::ImplItemKind::Fn(..)) - } -} - -impl MaybeFnLike for hir::TraitItem<'_> { - fn is_fn_like(&self) -> bool { - matches!(self.kind, hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_))) - } -} - -impl MaybeFnLike for hir::Expr<'_> { - fn is_fn_like(&self) -> bool { - matches!(self.kind, hir::ExprKind::Closure(..)) - } -} - -/// Carries either an FnLikeNode or an Expr, as these are the two -/// constructs that correspond to "code" (as in, something from which -/// we can construct a control-flow graph). -#[derive(Copy, Clone)] -pub enum Code<'a> { - FnLike(FnLikeNode<'a>), - Expr(&'a Expr<'a>), -} - -impl<'a> Code<'a> { - pub fn id(&self) -> hir::HirId { - match *self { - Code::FnLike(node) => node.id(), - Code::Expr(block) => block.hir_id, - } - } - - /// Attempts to construct a Code from presumed FnLike or Expr node input. - pub fn from_node(map: &Map<'a>, id: hir::HirId) -> Option<Code<'a>> { - match map.get(id) { - Node::Block(_) => { - // Use the parent, hopefully an expression node. - Code::from_node(map, map.get_parent_node(id)) - } - Node::Expr(expr) => Some(Code::Expr(expr)), - node => FnLikeNode::from_node(node).map(Code::FnLike), - } - } -} - -/// These are all the components one can extract from a fn item for -/// use when implementing FnLikeNode operations. -struct ItemFnParts<'a> { - ident: Ident, - decl: &'a hir::FnDecl<'a>, - header: hir::FnHeader, - vis: &'a hir::Visibility<'a>, - generics: &'a hir::Generics<'a>, - body: hir::BodyId, - id: hir::HirId, - span: Span, -} - -/// These are all the components one can extract from a closure expr -/// for use when implementing FnLikeNode operations. -struct ClosureParts<'a> { - decl: &'a FnDecl<'a>, - body: hir::BodyId, - id: hir::HirId, - span: Span, -} - -impl<'a> ClosureParts<'a> { - fn new(d: &'a FnDecl<'a>, b: hir::BodyId, id: hir::HirId, s: Span) -> Self { - ClosureParts { decl: d, body: b, id, span: s } - } -} - -impl<'a> FnLikeNode<'a> { - /// Attempts to construct a FnLikeNode from presumed FnLike node input. - pub fn from_node(node: Node<'_>) -> Option<FnLikeNode<'_>> { - let fn_like = match node { - Node::Item(item) => item.is_fn_like(), - Node::TraitItem(tm) => tm.is_fn_like(), - Node::ImplItem(it) => it.is_fn_like(), - Node::Expr(e) => e.is_fn_like(), - _ => false, - }; - fn_like.then_some(FnLikeNode { node }) - } - - pub fn body(self) -> hir::BodyId { - self.handle( - |i: ItemFnParts<'a>| i.body, - |_, _, _: &'a hir::FnSig<'a>, _, body: hir::BodyId, _| body, - |c: ClosureParts<'a>| c.body, - ) - } - - pub fn decl(self) -> &'a FnDecl<'a> { - self.handle( - |i: ItemFnParts<'a>| &*i.decl, - |_, _, sig: &'a hir::FnSig<'a>, _, _, _| &sig.decl, - |c: ClosureParts<'a>| c.decl, - ) - } - - pub fn span(self) -> Span { - self.handle( - |i: ItemFnParts<'_>| i.span, - |_, _, _: &'a hir::FnSig<'a>, _, _, span| span, - |c: ClosureParts<'_>| c.span, - ) - } - - pub fn id(self) -> hir::HirId { - self.handle( - |i: ItemFnParts<'_>| i.id, - |id, _, _: &'a hir::FnSig<'a>, _, _, _| id, - |c: ClosureParts<'_>| c.id, - ) - } - - pub fn constness(self) -> hir::Constness { - self.kind().header().map_or(hir::Constness::NotConst, |header| header.constness) - } - - pub fn asyncness(self) -> hir::IsAsync { - self.kind().header().map_or(hir::IsAsync::NotAsync, |header| header.asyncness) - } - - pub fn unsafety(self) -> hir::Unsafety { - self.kind().header().map_or(hir::Unsafety::Normal, |header| header.unsafety) - } - - pub fn kind(self) -> FnKind<'a> { - let item = |p: ItemFnParts<'a>| -> FnKind<'a> { - FnKind::ItemFn(p.ident, p.generics, p.header, p.vis) - }; - let closure = |_: ClosureParts<'a>| FnKind::Closure; - let method = - |_, ident: Ident, sig: &'a hir::FnSig<'a>, vis, _, _| FnKind::Method(ident, sig, vis); - self.handle(item, method, closure) - } - - fn handle<A, I, M, C>(self, item_fn: I, method: M, closure: C) -> A - where - I: FnOnce(ItemFnParts<'a>) -> A, - M: FnOnce( - hir::HirId, - Ident, - &'a hir::FnSig<'a>, - Option<&'a hir::Visibility<'a>>, - hir::BodyId, - Span, - ) -> A, - C: FnOnce(ClosureParts<'a>) -> A, - { - match self.node { - Node::Item(i) => match i.kind { - hir::ItemKind::Fn(ref sig, ref generics, block) => item_fn(ItemFnParts { - id: i.hir_id(), - ident: i.ident, - decl: &sig.decl, - body: block, - vis: &i.vis, - span: i.span, - header: sig.header, - generics, - }), - _ => bug!("item FnLikeNode that is not fn-like"), - }, - Node::TraitItem(ti) => match ti.kind { - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - method(ti.hir_id(), ti.ident, sig, None, body, ti.span) - } - _ => bug!("trait method FnLikeNode that is not fn-like"), - }, - Node::ImplItem(ii) => match ii.kind { - hir::ImplItemKind::Fn(ref sig, body) => { - method(ii.hir_id(), ii.ident, sig, Some(&ii.vis), body, ii.span) - } - _ => bug!("impl method FnLikeNode that is not fn-like"), - }, - Node::Expr(e) => match e.kind { - hir::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) => { - closure(ClosureParts::new(&decl, block, e.hir_id, e.span)) - } - _ => bug!("expr FnLikeNode that is not fn-like"), - }, - _ => bug!("other FnLikeNode that is not fn-like"), - } - } -} diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 38bc01b9b53..fad7e875fa1 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -20,8 +20,6 @@ use rustc_span::Span; use rustc_target::spec::abi::Abi; use std::collections::VecDeque; -pub mod blocks; - fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> { match node { Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 14a7e0ef170..8240273acad 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1,7 +1,7 @@ //! Type context book-keeping. use crate::arena::Arena; -use crate::dep_graph::DepGraph; +use crate::dep_graph::{DepGraph, DepKind, DepKindStruct}; use crate::hir::place::Place as HirPlace; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource}; @@ -79,11 +79,6 @@ pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync { where Self: Sized; - /// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation - /// session, if it still exists. This is used during incremental compilation to - /// turn a deserialized `DefPathHash` into its current `DefId`. - fn def_path_hash_to_def_id(&self, tcx: TyCtxt<'tcx>, def_path_hash: DefPathHash) -> DefId; - fn drop_serialized_data(&self, tcx: TyCtxt<'tcx>); fn serialize(&self, tcx: TyCtxt<'tcx>, encoder: &mut FileEncoder) -> FileEncodeResult; @@ -1016,6 +1011,7 @@ pub struct GlobalCtxt<'tcx> { pub queries: &'tcx dyn query::QueryEngine<'tcx>, pub query_caches: query::QueryCaches<'tcx>, + query_kinds: &'tcx [DepKindStruct], // Internal caches for metadata decoding. No need to track deps on this. pub ty_rcache: Lock<FxHashMap<ty::CReaderCacheKey, Ty<'tcx>>>, @@ -1149,6 +1145,7 @@ impl<'tcx> TyCtxt<'tcx> { dep_graph: DepGraph, on_disk_cache: Option<&'tcx dyn OnDiskCache<'tcx>>, queries: &'tcx dyn query::QueryEngine<'tcx>, + query_kinds: &'tcx [DepKindStruct], crate_name: &str, output_filenames: OutputFilenames, ) -> GlobalCtxt<'tcx> { @@ -1175,6 +1172,7 @@ impl<'tcx> TyCtxt<'tcx> { on_disk_cache, queries, query_caches: query::QueryCaches::default(), + query_kinds, ty_rcache: Default::default(), pred_rcache: Default::default(), selection_cache: Default::default(), @@ -1188,6 +1186,10 @@ impl<'tcx> TyCtxt<'tcx> { } } + crate fn query_kind(self, k: DepKind) -> &'tcx DepKindStruct { + &self.query_kinds[k as usize] + } + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used. #[track_caller] pub fn ty_error(self) -> Ty<'tcx> { @@ -1301,6 +1303,27 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation + /// session, if it still exists. This is used during incremental compilation to + /// turn a deserialized `DefPathHash` into its current `DefId`. + pub fn def_path_hash_to_def_id(self, hash: DefPathHash) -> DefId { + debug!("def_path_hash_to_def_id({:?})", hash); + + let stable_crate_id = hash.stable_crate_id(); + + // If this is a DefPathHash from the local crate, we can look up the + // DefId in the tcx's `Definitions`. + if stable_crate_id == self.sess.local_stable_crate_id() { + self.untracked_resolutions.definitions.local_def_path_hash_to_def_id(hash).to_def_id() + } else { + // If this is a DefPathHash from an upstream crate, let the CrateStore map + // it to a DefId. + let cstore = &self.untracked_resolutions.cstore; + let cnum = cstore.stable_crate_id_to_crate_num(stable_crate_id); + cstore.def_path_hash_to_def_id(cnum, hash) + } + } + pub fn def_path_debug_str(self, def_id: DefId) -> String { // We are explicitly not going through queries here in order to get // crate name and stable crate id since this code is called from debug!() diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index b1bc073ca99..6c1175ebdb4 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -102,6 +102,10 @@ impl TyCtxt<'tcx> { } } +/// Helper for `TyCtxtEnsure` to avoid a closure. +#[inline(always)] +fn noop<T>(_: &T) {} + macro_rules! query_helper_param_ty { (DefId) => { impl IntoQueryParam<DefId> }; ($K:ty) => { $K }; @@ -165,7 +169,7 @@ macro_rules! define_callbacks { #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) { let key = key.into_query_param(); - let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, |_| {}); + let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, noop); let lookup = match cached { Ok(()) => return, @@ -192,9 +196,7 @@ macro_rules! define_callbacks { pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> query_stored::$name<$tcx> { let key = key.into_query_param(); - let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, |value| { - value.clone() - }); + let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, Clone::clone); let lookup = match cached { Ok(value) => return value, diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index ef8bd20d510..e4c2d2dce67 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -2,7 +2,6 @@ use rustc_data_structures::graph::iterate::{ NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, }; use rustc_hir::intravisit::FnKind; -use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::mir::{BasicBlock, Body, Operand, TerminatorKind}; use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; use rustc_middle::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt}; @@ -14,8 +13,8 @@ crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let def_id = body.source.def_id().expect_local(); let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - if let Some(fn_like_node) = FnLikeNode::from_node(tcx.hir().get(hir_id)) { - if let FnKind::Closure = fn_like_node.kind() { + if let Some(fn_kind) = tcx.hir().get(hir_id).fn_kind() { + if let FnKind::Closure = fn_kind { // closures can't recur, so they don't matter. return; } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 847b89f0464..dd16e3cde75 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -130,6 +130,9 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { traits::NonStructuralMatchTy::Opaque => { "opaque types cannot be used in patterns".to_string() } + traits::NonStructuralMatchTy::Closure => { + "closures cannot be used in patterns".to_string() + } traits::NonStructuralMatchTy::Generator => { "generators cannot be used in patterns".to_string() } diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 17790ec91c8..63c637af5c2 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -68,11 +68,10 @@ impl<'tcx> MirPass<'tcx> for ConstProp { return; } - use rustc_middle::hir::map::blocks::FnLikeNode; let def_id = body.source.def_id().expect_local(); let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); + let is_fn_like = tcx.hir().get(hir_id).fn_kind().is_some(); let is_assoc_const = tcx.def_kind(def_id.to_def_id()) == DefKind::AssocConst; // Only run const prop on functions, methods, closures and associated constants diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index e980d3d884f..4ac93f71619 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -19,7 +19,6 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_index::vec::IndexVec; use rustc_middle::hir; -use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::*; use rustc_middle::mir::dump_enabled; @@ -64,7 +63,7 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { } let hir_id = tcx.hir().local_def_id_to_hir_id(mir_source.def_id().expect_local()); - let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); + let is_fn_like = tcx.hir().get(hir_id).fn_kind().is_some(); // Only instrument functions, methods, and closures (not constants since they are evaluated // at compile time by Miri). @@ -74,7 +73,7 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { // be tricky if const expressions have no corresponding statements in the enclosing MIR. // Closures are carved out by their initial `Assign` statement.) if !is_fn_like { - trace!("InstrumentCoverage skipped for {:?} (not an FnLikeNode)", mir_source.def_id()); + trace!("InstrumentCoverage skipped for {:?} (not an fn-like)", mir_source.def_id()); return; } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 60135ef2d85..ab1fe6fe077 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -429,8 +429,7 @@ fn mir_drops_elaborated_and_const_checked<'tcx>( } let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); - use rustc_middle::hir::map::blocks::FnLikeNode; - let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); + let is_fn_like = tcx.hir().get(hir_id).fn_kind().is_some(); if is_fn_like { let did = def.did.to_def_id(); let def = ty::WithOptConstParam::unknown(did); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 1d9c3a4f3cf..d2167c7a5db 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -279,9 +279,9 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Macro) { // MACROS 2.0 ITEM self.parse_item_decl_macro(lo)? - } else if self.is_macro_rules_item() { + } else if let IsMacroRulesItem::Yes { has_bang } = self.is_macro_rules_item() { // MACRO_RULES ITEM - self.parse_item_macro_rules(vis)? + self.parse_item_macro_rules(vis, has_bang)? } else if vis.kind.is_pub() && self.isnt_macro_invocation() { self.recover_missing_kw_before_item()?; return Ok(None); @@ -300,7 +300,7 @@ impl<'a> Parser<'a> { || self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }` || self.check_auto_or_unsafe_trait_item() // no: `auto::b`, yes: `auto trait X { .. }` || self.is_async_fn() // no(2015): `async::b`, yes: `async fn` - || self.is_macro_rules_item() // no: `macro_rules::b`, yes: `macro_rules! mac` + || matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac` } /// Are we sure this could not possibly be a macro invocation? @@ -1534,18 +1534,43 @@ impl<'a> Parser<'a> { Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, macro_rules: false }))) } - /// Is this unambiguously the start of a `macro_rules! foo` item definition? - fn is_macro_rules_item(&mut self) -> bool { - self.check_keyword(kw::MacroRules) - && self.look_ahead(1, |t| *t == token::Not) - && self.look_ahead(2, |t| t.is_ident()) + /// Is this a possibly malformed start of a `macro_rules! foo` item definition? + + fn is_macro_rules_item(&mut self) -> IsMacroRulesItem { + if self.check_keyword(kw::MacroRules) { + let macro_rules_span = self.token.span; + + if self.look_ahead(1, |t| *t == token::Not) && self.look_ahead(2, |t| t.is_ident()) { + return IsMacroRulesItem::Yes { has_bang: true }; + } else if self.look_ahead(1, |t| (t.is_ident())) { + // macro_rules foo + self.struct_span_err(macro_rules_span, "expected `!` after `macro_rules`") + .span_suggestion( + macro_rules_span, + "add a `!`", + "macro_rules!".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + + return IsMacroRulesItem::Yes { has_bang: false }; + } + } + + IsMacroRulesItem::No } /// Parses a `macro_rules! foo { ... }` declarative macro. - fn parse_item_macro_rules(&mut self, vis: &Visibility) -> PResult<'a, ItemInfo> { + fn parse_item_macro_rules( + &mut self, + vis: &Visibility, + has_bang: bool, + ) -> PResult<'a, ItemInfo> { self.expect_keyword(kw::MacroRules)?; // `macro_rules` - self.expect(&token::Not)?; // `!` + if has_bang { + self.expect(&token::Not)?; // `!` + } let ident = self.parse_ident()?; if self.eat(&token::Not) { @@ -2121,3 +2146,8 @@ impl<'a> Parser<'a> { } } } + +enum IsMacroRulesItem { + Yes { has_bang: bool }, + No, +} diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index ae3a9c71c59..4d4e9432e87 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -23,7 +23,7 @@ use rustc_middle::span_bug; use rustc_middle::thir::abstract_const::Node as ACNode; use rustc_middle::ty::fold::TypeVisitor; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable}; use rustc_session::lint; use rustc_span::hygiene::Transparency; @@ -153,11 +153,8 @@ where tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, ) -> ControlFlow<V::BreakTy> { - const_evaluatable::walk_abstract_const(tcx, ct, |node| match node.root() { - ACNode::Leaf(leaf) => { - let leaf = leaf.subst(tcx, ct.substs); - self.visit_const(leaf) - } + const_evaluatable::walk_abstract_const(tcx, ct, |node| match node.root(tcx) { + ACNode::Leaf(leaf) => self.visit_const(leaf), ACNode::Cast(_, _, ty) => self.visit_ty(ty), ACNode::Binop(..) | ACNode::UnaryOp(..) | ACNode::FunctionCall(_, _) => { ControlFlow::CONTINUE diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index 89df3d4674b..f984bb1872b 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -7,9 +7,8 @@ edition = "2021" doctest = false [dependencies] -measureme = "9.0.0" +measureme = "10.0.0" rustc-rayon-core = "0.3.1" -tracing = "0.1" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index e50a8c191ad..b216d78da94 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -13,13 +13,12 @@ extern crate rustc_macros; #[macro_use] extern crate rustc_middle; -#[macro_use] -extern crate tracing; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_errors::DiagnosticBuilder; -use rustc_middle::dep_graph; +use rustc_middle::arena::Arena; +use rustc_middle::dep_graph::{self, DepKindStruct}; use rustc_middle::ty::query::{query_keys, query_storage, query_stored, query_values}; use rustc_middle::ty::query::{Providers, QueryEngine}; use rustc_middle::ty::{self, TyCtxt}; @@ -29,7 +28,6 @@ use rustc_span::Span; #[macro_use] mod plumbing; pub use plumbing::QueryCtxt; -use plumbing::QueryStruct; use rustc_query_system::query::*; mod stats; diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index 48eb488792d..86b12b3586a 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -219,7 +219,7 @@ impl<'sess> rustc_middle::ty::OnDiskCache<'sess> for OnDiskCache<'sess> { // Do this *before* we clone 'latest_foreign_def_path_hashes', since // loading existing queries may cause us to create new DepNodes, which // may in turn end up invoking `store_foreign_def_id_hash` - tcx.dep_graph.exec_cache_promotions(QueryCtxt::from_tcx(tcx)); + tcx.dep_graph.exec_cache_promotions(tcx); *self.serialized_data.write() = None; } @@ -358,23 +358,6 @@ impl<'sess> rustc_middle::ty::OnDiskCache<'sess> for OnDiskCache<'sess> { Ok(()) }) } - - fn def_path_hash_to_def_id(&self, tcx: TyCtxt<'tcx>, hash: DefPathHash) -> DefId { - debug!("def_path_hash_to_def_id({:?})", hash); - - let stable_crate_id = hash.stable_crate_id(); - - // If this is a DefPathHash from the local crate, we can look up the - // DefId in the tcx's `Definitions`. - if stable_crate_id == tcx.sess.local_stable_crate_id() { - tcx.definitions_untracked().local_def_path_hash_to_def_id(hash).to_def_id() - } else { - // If this is a DefPathHash from an upstream crate, let the CrateStore map - // it to a DefId. - let cnum = tcx.cstore_untracked().stable_crate_id_to_crate_num(stable_crate_id); - tcx.cstore_untracked().def_path_hash_to_def_id(cnum, hash) - } - } } impl<'sess> OnDiskCache<'sess> { @@ -764,7 +747,7 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for DefId { // If we get to this point, then all of the query inputs were green, // which means that the definition with this hash is guaranteed to // still exist in the current compilation session. - Ok(d.tcx().on_disk_cache.as_ref().unwrap().def_path_hash_to_def_id(d.tcx(), def_path_hash)) + Ok(d.tcx().def_path_hash_to_def_id(def_path_hash)) } } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 4d1e39db0ed..8c3fbb2071c 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -3,7 +3,7 @@ //! manage the caches, and so forth. use crate::{on_disk_cache, queries, Queries}; -use rustc_middle::dep_graph::{DepKind, DepNode, DepNodeIndex, SerializedDepNodeIndex}; +use rustc_middle::dep_graph::{DepKind, DepNodeIndex, SerializedDepNodeIndex}; use rustc_middle::ty::tls::{self, ImplicitCtxt}; use rustc_middle::ty::{self, TyCtxt}; use rustc_query_system::dep_graph::HasDepContext; @@ -53,36 +53,6 @@ impl QueryContext for QueryCtxt<'tcx> { self.queries.try_collect_active_jobs(**self) } - fn try_load_from_on_disk_cache(&self, dep_node: &DepNode) { - let cb = &super::QUERY_CALLBACKS[dep_node.kind as usize]; - (cb.try_load_from_on_disk_cache)(*self, dep_node) - } - - fn try_force_from_dep_node(&self, dep_node: &DepNode) -> bool { - debug!("try_force_from_dep_node({:?}) --- trying to force", dep_node); - - // We must avoid ever having to call `force_from_dep_node()` for a - // `DepNode::codegen_unit`: - // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we - // would always end up having to evaluate the first caller of the - // `codegen_unit` query that *is* reconstructible. This might very well be - // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just - // to re-trigger calling the `codegen_unit` query with the right key. At - // that point we would already have re-done all the work we are trying to - // avoid doing in the first place. - // The solution is simple: Just explicitly call the `codegen_unit` query for - // each CGU, right after partitioning. This way `try_mark_green` will always - // hit the cache instead of having to go through `force_from_dep_node`. - // This assertion makes sure, we actually keep applying the solution above. - debug_assert!( - dep_node.kind != DepKind::codegen_unit, - "calling force_from_dep_node() on DepKind::codegen_unit" - ); - - let cb = &super::QUERY_CALLBACKS[dep_node.kind as usize]; - (cb.force_from_dep_node)(*self, dep_node) - } - // Interactions with on_disk_cache fn load_side_effects(&self, prev_dep_node_index: SerializedDepNodeIndex) -> QuerySideEffects { self.queries @@ -193,60 +163,6 @@ impl<'tcx> QueryCtxt<'tcx> { } } -/// This struct stores metadata about each Query. -/// -/// Information is retrieved by indexing the `QUERIES` array using the integer value -/// of the `DepKind`. Overall, this allows to implement `QueryContext` using this manual -/// jump table instead of large matches. -pub struct QueryStruct { - /// The red/green evaluation system will try to mark a specific DepNode in the - /// dependency graph as green by recursively trying to mark the dependencies of - /// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` - /// where we don't know if it is red or green and we therefore actually have - /// to recompute its value in order to find out. Since the only piece of - /// information that we have at that point is the `DepNode` we are trying to - /// re-evaluate, we need some way to re-run a query from just that. This is what - /// `force_from_dep_node()` implements. - /// - /// In the general case, a `DepNode` consists of a `DepKind` and an opaque - /// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint - /// is usually constructed by computing a stable hash of the query-key that the - /// `DepNode` corresponds to. Consequently, it is not in general possible to go - /// back from hash to query-key (since hash functions are not reversible). For - /// this reason `force_from_dep_node()` is expected to fail from time to time - /// because we just cannot find out, from the `DepNode` alone, what the - /// corresponding query-key is and therefore cannot re-run the query. - /// - /// The system deals with this case letting `try_mark_green` fail which forces - /// the root query to be re-evaluated. - /// - /// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. - /// Fortunately, we can use some contextual information that will allow us to - /// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we - /// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a - /// valid `DefPathHash`. Since we also always build a huge table that maps every - /// `DefPathHash` in the current codebase to the corresponding `DefId`, we have - /// everything we need to re-run the query. - /// - /// Take the `mir_promoted` query as an example. Like many other queries, it - /// just has a single parameter: the `DefId` of the item it will compute the - /// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` - /// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` - /// is actually a `DefPathHash`, and can therefore just look up the corresponding - /// `DefId` in `tcx.def_path_hash_to_def_id`. - /// - /// When you implement a new query, it will likely have a corresponding new - /// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As - /// a rule of thumb, if your query takes a `DefId` or `LocalDefId` as sole parameter, - /// then `force_from_dep_node()` should not fail for it. Otherwise, you can just - /// add it to the "We don't have enough information to reconstruct..." group in - /// the match below. - pub(crate) force_from_dep_node: fn(tcx: QueryCtxt<'_>, dep_node: &DepNode) -> bool, - - /// Invoke a query to put the on-disk cached value in memory. - pub(crate) try_load_from_on_disk_cache: fn(QueryCtxt<'_>, &DepNode), -} - macro_rules! handle_cycle_error { ([][$tcx: expr, $error:expr]) => {{ $error.emit(); @@ -291,14 +207,14 @@ macro_rules! is_eval_always { } macro_rules! hash_result { - ([][$hcx:expr, $result:expr]) => {{ - dep_graph::hash_result($hcx, &$result) + ([]) => {{ + Some(dep_graph::hash_result) }}; - ([(no_hash) $($rest:tt)*][$hcx:expr, $result:expr]) => {{ + ([(no_hash) $($rest:tt)*]) => {{ None }}; - ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { - hash_result!([$($modifiers)*][$($args)*]) + ([$other:tt $($modifiers:tt)*]) => { + hash_result!([$($modifiers)*]) }; } @@ -378,6 +294,7 @@ macro_rules! define_queries { const ANON: bool = is_anon!([$($modifiers)*]); const EVAL_ALWAYS: bool = is_eval_always!([$($modifiers)*]); const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$name; + const HASH_RESULT: Option<fn(&mut StableHashingContext<'_>, &Self::Value) -> Fingerprint> = hash_result!([$($modifiers)*]); type Cache = query_storage::$name<$tcx>; @@ -406,13 +323,6 @@ macro_rules! define_queries { } } - fn hash_result( - _hcx: &mut StableHashingContext<'_>, - _result: &Self::Value - ) -> Option<Fingerprint> { - hash_result!([$($modifiers)*][_hcx, _result]) - } - fn handle_cycle_error( tcx: QueryCtxt<'tcx>, mut error: DiagnosticBuilder<'_>, @@ -421,7 +331,7 @@ macro_rules! define_queries { } })* - #[allow(non_upper_case_globals)] + #[allow(nonstandard_style)] pub mod query_callbacks { use super::*; use rustc_middle::dep_graph::DepNode; @@ -431,68 +341,101 @@ macro_rules! define_queries { use rustc_query_system::dep_graph::FingerprintStyle; // We use this for most things when incr. comp. is turned off. - pub const Null: QueryStruct = QueryStruct { - force_from_dep_node: |_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node), - try_load_from_on_disk_cache: |_, _| {}, - }; + pub fn Null() -> DepKindStruct { + DepKindStruct { + is_anon: false, + is_eval_always: false, + fingerprint_style: FingerprintStyle::Unit, + force_from_dep_node: Some(|_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node)), + try_load_from_on_disk_cache: None, + } + } - pub const TraitSelect: QueryStruct = QueryStruct { - force_from_dep_node: |_, _| false, - try_load_from_on_disk_cache: |_, _| {}, - }; + pub fn TraitSelect() -> DepKindStruct { + DepKindStruct { + is_anon: true, + is_eval_always: false, + fingerprint_style: FingerprintStyle::Unit, + force_from_dep_node: None, + try_load_from_on_disk_cache: None, + } + } - pub const CompileCodegenUnit: QueryStruct = QueryStruct { - force_from_dep_node: |_, _| false, - try_load_from_on_disk_cache: |_, _| {}, - }; + pub fn CompileCodegenUnit() -> DepKindStruct { + DepKindStruct { + is_anon: false, + is_eval_always: false, + fingerprint_style: FingerprintStyle::Opaque, + force_from_dep_node: None, + try_load_from_on_disk_cache: None, + } + } - pub const CompileMonoItem: QueryStruct = QueryStruct { - force_from_dep_node: |_, _| false, - try_load_from_on_disk_cache: |_, _| {}, - }; + pub fn CompileMonoItem() -> DepKindStruct { + DepKindStruct { + is_anon: false, + is_eval_always: false, + fingerprint_style: FingerprintStyle::Opaque, + force_from_dep_node: None, + try_load_from_on_disk_cache: None, + } + } - $(pub const $name: QueryStruct = { - const is_anon: bool = is_anon!([$($modifiers)*]); + $(pub fn $name()-> DepKindStruct { + let is_anon = is_anon!([$($modifiers)*]); + let is_eval_always = is_eval_always!([$($modifiers)*]); - #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - <query_keys::$name<'_> as DepNodeParams<TyCtxt<'_>>> - ::fingerprint_style() - } + let fingerprint_style = + <query_keys::$name<'_> as DepNodeParams<TyCtxt<'_>>>::fingerprint_style(); - fn recover<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<query_keys::$name<'tcx>> { - <query_keys::$name<'_> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node) + if is_anon || !fingerprint_style.reconstructible() { + return DepKindStruct { + is_anon, + is_eval_always, + fingerprint_style, + force_from_dep_node: None, + try_load_from_on_disk_cache: None, + } } - fn force_from_dep_node(tcx: QueryCtxt<'_>, dep_node: &DepNode) -> bool { - force_query::<queries::$name<'_>, _>(tcx, dep_node) + #[inline(always)] + fn recover<'tcx>(tcx: TyCtxt<'tcx>, dep_node: DepNode) -> Option<query_keys::$name<'tcx>> { + <query_keys::$name<'_> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, &dep_node) } - fn try_load_from_on_disk_cache(tcx: QueryCtxt<'_>, dep_node: &DepNode) { - if is_anon { - return - } - - if !fingerprint_style().reconstructible() { - return + fn force_from_dep_node(tcx: TyCtxt<'_>, dep_node: DepNode) -> bool { + if let Some(key) = recover(tcx, dep_node) { + let tcx = QueryCtxt::from_tcx(tcx); + force_query::<queries::$name<'_>, _>(tcx, key, dep_node); + true + } else { + false } + } - debug_assert!(tcx.dep_graph.is_green(dep_node)); + fn try_load_from_on_disk_cache(tcx: TyCtxt<'_>, dep_node: DepNode) { + debug_assert!(tcx.dep_graph.is_green(&dep_node)); - let key = recover(*tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash)); + let key = recover(tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash)); + let tcx = QueryCtxt::from_tcx(tcx); if queries::$name::cache_on_disk(tcx, &key, None) { let _ = tcx.$name(key); } } - QueryStruct { - force_from_dep_node, - try_load_from_on_disk_cache, + DepKindStruct { + is_anon, + is_eval_always, + fingerprint_style, + force_from_dep_node: Some(force_from_dep_node), + try_load_from_on_disk_cache: Some(try_load_from_on_disk_cache), } - };)* + })* } - static QUERY_CALLBACKS: &[QueryStruct] = &make_dep_kind_array!(query_callbacks); + pub fn query_callbacks<'tcx>(arena: &'tcx Arena<'tcx>) -> &'tcx [DepKindStruct] { + arena.alloc_from_iter(make_dep_kind_array!(query_callbacks)) + } } } diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index 8602219a7f3..c274c2cc26c 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -60,8 +60,11 @@ impl<K: DepKind> DepNode<K> { /// Creates a new, parameterless DepNode. This method will assert /// that the DepNode corresponding to the given DepKind actually /// does not require any parameters. - pub fn new_no_params(kind: K) -> DepNode<K> { - debug_assert!(!kind.has_params()); + pub fn new_no_params<Ctxt>(tcx: Ctxt, kind: K) -> DepNode<K> + where + Ctxt: super::DepContext<DepKind = K>, + { + debug_assert_eq!(tcx.fingerprint_style(kind), FingerprintStyle::Unit); DepNode { kind, hash: Fingerprint::ZERO.into() } } @@ -75,7 +78,7 @@ impl<K: DepKind> DepNode<K> { #[cfg(debug_assertions)] { - if !kind.fingerprint_style().reconstructible() + if !tcx.fingerprint_style(kind).reconstructible() && (tcx.sess().opts.debugging_opts.incremental_info || tcx.sess().opts.debugging_opts.query_dep_graph) { @@ -121,11 +124,12 @@ impl<Ctxt: DepContext, T> DepNodeParams<Ctxt> for T where T: for<'a> HashStable<StableHashingContext<'a>> + fmt::Debug, { - #[inline] + #[inline(always)] default fn fingerprint_style() -> FingerprintStyle { FingerprintStyle::Opaque } + #[inline(always)] default fn to_fingerprint(&self, tcx: Ctxt) -> Fingerprint { let mut hcx = tcx.create_stable_hashing_context(); let mut hasher = StableHasher::new(); @@ -135,10 +139,12 @@ where hasher.finish() } + #[inline(always)] default fn to_debug_str(&self, _: Ctxt) -> String { format!("{:?}", *self) } + #[inline(always)] default fn recover(_: Ctxt, _: &DepNode<Ctxt::DepKind>) -> Option<Self> { None } diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 114d12fb90b..a8be1ca34c0 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -33,12 +33,6 @@ pub struct DepGraph<K: DepKind> { /// each task has a `DepNodeIndex` that uniquely identifies it. This unique /// ID is used for self-profiling. virtual_dep_node_index: Lrc<AtomicU32>, - - /// The cached event id for profiling node interning. This saves us - /// from having to look up the event id every time we intern a node - /// which may incur too much overhead. - /// This will be None if self-profiling is disabled. - node_intern_event_id: Option<EventId>, } rustc_index::newtype_index! { @@ -96,14 +90,13 @@ struct DepGraphData<K: DepKind> { dep_node_debug: Lock<FxHashMap<DepNode<K>, String>>, } -pub fn hash_result<R>(hcx: &mut StableHashingContext<'_>, result: &R) -> Option<Fingerprint> +pub fn hash_result<R>(hcx: &mut StableHashingContext<'_>, result: &R) -> Fingerprint where R: for<'a> HashStable<StableHashingContext<'a>>, { let mut stable_hasher = StableHasher::new(); result.hash_stable(hcx, &mut stable_hasher); - - Some(stable_hasher.finish()) + stable_hasher.finish() } impl<K: DepKind> DepGraph<K> { @@ -117,8 +110,13 @@ impl<K: DepKind> DepGraph<K> { ) -> DepGraph<K> { let prev_graph_node_count = prev_graph.node_count(); - let current = - CurrentDepGraph::new(prev_graph_node_count, encoder, record_graph, record_stats); + let current = CurrentDepGraph::new( + profiler, + prev_graph_node_count, + encoder, + record_graph, + record_stats, + ); // Instantiate a dependy-less node only once for anonymous queries. let _green_node_index = current.intern_new_node( @@ -129,10 +127,6 @@ impl<K: DepKind> DepGraph<K> { ); debug_assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE); - let node_intern_event_id = profiler - .get_or_alloc_cached_string("incr_comp_intern_dep_graph_node") - .map(EventId::from_label); - DepGraph { data: Some(Lrc::new(DepGraphData { previous_work_products: prev_work_products, @@ -143,16 +137,11 @@ impl<K: DepKind> DepGraph<K> { colors: DepNodeColorMap::new(prev_graph_node_count), })), virtual_dep_node_index: Lrc::new(AtomicU32::new(0)), - node_intern_event_id, } } pub fn new_disabled() -> DepGraph<K> { - DepGraph { - data: None, - virtual_dep_node_index: Lrc::new(AtomicU32::new(0)), - node_intern_event_id: None, - } + DepGraph { data: None, virtual_dep_node_index: Lrc::new(AtomicU32::new(0)) } } /// Returns `true` if we are actually building the full dep-graph, and `false` otherwise. @@ -215,7 +204,7 @@ impl<K: DepKind> DepGraph<K> { cx: Ctxt, arg: A, task: fn(Ctxt, A) -> R, - hash_result: fn(&mut StableHashingContext<'_>, &R) -> Option<Fingerprint>, + hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>, ) -> (R, DepNodeIndex) { if self.is_fully_enabled() { self.with_task_impl(key, cx, arg, task, hash_result) @@ -234,7 +223,7 @@ impl<K: DepKind> DepGraph<K> { cx: Ctxt, arg: A, task: fn(Ctxt, A) -> R, - hash_result: fn(&mut StableHashingContext<'_>, &R) -> Option<Fingerprint>, + hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>, ) -> (R, DepNodeIndex) { // This function is only called when the graph is enabled. let data = self.data.as_ref().unwrap(); @@ -253,7 +242,7 @@ impl<K: DepKind> DepGraph<K> { key ); - let task_deps = if key.kind.is_eval_always() { + let task_deps = if cx.dep_context().is_eval_always(key.kind) { None } else { Some(Lock::new(TaskDeps { @@ -268,15 +257,14 @@ impl<K: DepKind> DepGraph<K> { let edges = task_deps.map_or_else(|| smallvec![], |lock| lock.into_inner().reads); let dcx = cx.dep_context(); - let mut hcx = dcx.create_stable_hashing_context(); let hashing_timer = dcx.profiler().incr_result_hashing(); - let current_fingerprint = hash_result(&mut hcx, &result); + let current_fingerprint = hash_result.map(|f| { + let mut hcx = dcx.create_stable_hashing_context(); + f(&mut hcx, &result) + }); let print_status = cfg!(debug_assertions) && dcx.sess().opts.debugging_opts.dep_tasks; - // Get timer for profiling `DepNode` interning - let node_intern_timer = - self.node_intern_event_id.map(|eid| dcx.profiler().generic_activity_with_event_id(eid)); // Intern the new `DepNode`. let (dep_node_index, prev_and_color) = data.current.intern_node( dcx.profiler(), @@ -286,7 +274,6 @@ impl<K: DepKind> DepGraph<K> { current_fingerprint, print_status, ); - drop(node_intern_timer); hashing_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -315,7 +302,7 @@ impl<K: DepKind> DepGraph<K> { where OP: FnOnce() -> R, { - debug_assert!(!dep_kind.is_eval_always()); + debug_assert!(!cx.is_eval_always(dep_kind)); if let Some(ref data) = self.data { let task_deps = Lock::new(TaskDeps::default()); @@ -492,7 +479,7 @@ impl<K: DepKind> DepGraph<K> { tcx: Ctxt, dep_node: &DepNode<K>, ) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> { - debug_assert!(!dep_node.kind.is_eval_always()); + debug_assert!(!tcx.dep_context().is_eval_always(dep_node.kind)); // Return None if the dep graph is disabled let data = self.data.as_ref()?; @@ -552,7 +539,7 @@ impl<K: DepKind> DepGraph<K> { // We don't know the state of this dependency. If it isn't // an eval_always node, let's try to mark it green recursively. - if !dep_dep_node.kind.is_eval_always() { + if !tcx.dep_context().is_eval_always(dep_dep_node.kind) { debug!( "try_mark_previous_green({:?}) --- state of dependency {:?} ({}) \ is unknown, trying to mark it green", @@ -575,7 +562,7 @@ impl<K: DepKind> DepGraph<K> { "try_mark_previous_green({:?}) --- trying to force dependency {:?}", dep_node, dep_dep_node ); - if !tcx.try_force_from_dep_node(dep_dep_node) { + if !tcx.dep_context().try_force_from_dep_node(*dep_dep_node) { // The DepNode could not be forced. debug!( "try_mark_previous_green({:?}) - END - dependency {:?} could not be forced", @@ -642,7 +629,7 @@ impl<K: DepKind> DepGraph<K> { } // We never try to mark eval_always nodes as green - debug_assert!(!dep_node.kind.is_eval_always()); + debug_assert!(!tcx.dep_context().is_eval_always(dep_node.kind)); debug_assert_eq!(data.previous.index_to_node(prev_dep_node_index), *dep_node); @@ -740,8 +727,7 @@ impl<K: DepKind> DepGraph<K> { // // This method will only load queries that will end up in the disk cache. // Other queries will not be executed. - pub fn exec_cache_promotions<Ctxt: QueryContext<DepKind = K>>(&self, qcx: Ctxt) { - let tcx = qcx.dep_context(); + pub fn exec_cache_promotions<Ctxt: DepContext<DepKind = K>>(&self, tcx: Ctxt) { let _prof_timer = tcx.profiler().generic_activity("incr_comp_query_cache_promotion"); let data = self.data.as_ref().unwrap(); @@ -749,7 +735,7 @@ impl<K: DepKind> DepGraph<K> { match data.colors.get(prev_index) { Some(DepNodeColor::Green(_)) => { let dep_node = data.previous.index_to_node(prev_index); - qcx.try_load_from_on_disk_cache(&dep_node); + tcx.try_load_from_on_disk_cache(dep_node); } None | Some(DepNodeColor::Red) => { // We can skip red nodes because a node can only be marked @@ -876,10 +862,17 @@ pub(super) struct CurrentDepGraph<K: DepKind> { /// debugging and only active with `debug_assertions`. total_read_count: AtomicU64, total_duplicate_read_count: AtomicU64, + + /// The cached event id for profiling node interning. This saves us + /// from having to look up the event id every time we intern a node + /// which may incur too much overhead. + /// This will be None if self-profiling is disabled. + node_intern_event_id: Option<EventId>, } impl<K: DepKind> CurrentDepGraph<K> { fn new( + profiler: &SelfProfilerRef, prev_graph_node_count: usize, encoder: FileEncoder, record_graph: bool, @@ -908,6 +901,10 @@ impl<K: DepKind> CurrentDepGraph<K> { let new_node_count_estimate = 102 * prev_graph_node_count / 100 + 200; + let node_intern_event_id = profiler + .get_or_alloc_cached_string("incr_comp_intern_dep_graph_node") + .map(EventId::from_label); + CurrentDepGraph { encoder: Steal::new(GraphEncoder::new( encoder, @@ -927,6 +924,7 @@ impl<K: DepKind> CurrentDepGraph<K> { forbidden_edge, total_read_count: AtomicU64::new(0), total_duplicate_read_count: AtomicU64::new(0), + node_intern_event_id, } } @@ -970,6 +968,10 @@ impl<K: DepKind> CurrentDepGraph<K> { ) -> (DepNodeIndex, Option<(SerializedDepNodeIndex, DepNodeColor)>) { let print_status = cfg!(debug_assertions) && print_status; + // Get timer for profiling `DepNode` interning + let _node_intern_timer = + self.node_intern_event_id.map(|eid| profiler.generic_activity_with_event_id(eid)); + if let Some(prev_index) = prev_graph.node_to_index_opt(&key) { // Determine the color and index of the new `DepNode`. if let Some(fingerprint) = fingerprint { diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index dcda5728334..047fc9f10cc 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -32,6 +32,17 @@ pub trait DepContext: Copy { /// Access the compiler session. fn sess(&self) -> &Session; + + /// Return whether this kind always require evaluation. + fn is_eval_always(&self, kind: Self::DepKind) -> bool; + + fn fingerprint_style(&self, kind: Self::DepKind) -> FingerprintStyle; + + /// Try to force a dep node to execute and see if it's green. + fn try_force_from_dep_node(&self, dep_node: DepNode<Self::DepKind>) -> bool; + + /// Load data from the on-disk cache. + fn try_load_from_on_disk_cache(&self, dep_node: DepNode<Self::DepKind>); } pub trait HasDepContext: Copy { @@ -51,7 +62,7 @@ impl<T: DepContext> HasDepContext for T { } /// Describes the contents of the fingerprint generated by a given query. -#[derive(PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum FingerprintStyle { /// The fingerprint is actually a DefPathHash. DefPathHash, @@ -75,12 +86,6 @@ impl FingerprintStyle { pub trait DepKind: Copy + fmt::Debug + Eq + Hash + Send + Encodable<FileEncoder> + 'static { const NULL: Self; - /// Return whether this kind always require evaluation. - fn is_eval_always(&self) -> bool; - - /// Return whether this kind requires additional parameters to be executed. - fn has_params(&self) -> bool; - /// Implementation of `std::fmt::Debug` for `DepNode`. fn debug_node(node: &DepNode<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result; @@ -93,6 +98,4 @@ pub trait DepKind: Copy + fmt::Debug + Eq + Hash + Send + Encodable<FileEncoder> fn read_deps<OP>(op: OP) where OP: for<'a> FnOnce(Option<&'a Lock<TaskDeps<Self>>>); - - fn fingerprint_style(&self) -> FingerprintStyle; } diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index f5f67fcd0a0..47197a1e492 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -222,7 +222,7 @@ impl<K: DepKind> EncoderState<K> { index } - fn finish(self) -> FileEncodeResult { + fn finish(self, profiler: &SelfProfilerRef) -> FileEncodeResult { let Self { mut encoder, total_node_count, total_edge_count, result, stats: _ } = self; let () = result?; @@ -235,7 +235,11 @@ impl<K: DepKind> EncoderState<K> { IntEncodedWithFixedSize(edge_count).encode(&mut encoder)?; debug!("position: {:?}", encoder.position()); // Drop the encoder so that nothing is written after the counts. - encoder.flush() + let result = encoder.flush(); + // FIXME(rylev): we hardcode the dep graph file name so we don't need a dependency on + // rustc_incremental just for that. + profiler.artifact_size("dep_graph", "dep-graph.bin", encoder.position() as u64); + result } } @@ -332,6 +336,6 @@ impl<K: DepKind + Encodable<FileEncoder>> GraphEncoder<K> { pub fn finish(self, profiler: &SelfProfilerRef) -> FileEncodeResult { let _prof_timer = profiler.generic_activity("incr_comp_encode_dep_graph"); - self.status.into_inner().finish() + self.status.into_inner().finish(profiler) } } diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs index 76a165ed8e6..fc3b7980dfb 100644 --- a/compiler/rustc_query_system/src/query/config.rs +++ b/compiler/rustc_query_system/src/query/config.rs @@ -24,7 +24,8 @@ pub(crate) struct QueryVtable<CTX: QueryContext, K, V> { pub dep_kind: CTX::DepKind, pub eval_always: bool, - pub hash_result: fn(&mut StableHashingContext<'_>, &V) -> Option<Fingerprint>, + pub compute: fn(CTX::DepContext, K) -> V, + pub hash_result: Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerprint>, pub handle_cycle_error: fn(CTX, DiagnosticBuilder<'_>) -> V, pub cache_on_disk: fn(CTX, &K, Option<&V>) -> bool, pub try_load_from_disk: fn(CTX, SerializedDepNodeIndex) -> Option<V>, @@ -38,12 +39,8 @@ impl<CTX: QueryContext, K, V> QueryVtable<CTX, K, V> { DepNode::construct(tcx, self.dep_kind, key) } - pub(crate) fn hash_result( - &self, - hcx: &mut StableHashingContext<'_>, - value: &V, - ) -> Option<Fingerprint> { - (self.hash_result)(hcx, value) + pub(crate) fn compute(&self, tcx: CTX::DepContext, key: K) -> V { + (self.compute)(tcx, key) } pub(crate) fn cache_on_disk(&self, tcx: CTX, key: &K, value: Option<&V>) -> bool { @@ -59,6 +56,9 @@ pub trait QueryAccessors<CTX: QueryContext>: QueryConfig { const ANON: bool; const EVAL_ALWAYS: bool; const DEP_KIND: CTX::DepKind; + const HASH_RESULT: Option< + fn(hcx: &mut StableHashingContext<'_>, result: &Self::Value) -> Fingerprint, + >; type Cache: QueryCache<Key = Self::Key, Stored = Self::Stored, Value = Self::Value>; @@ -75,9 +75,6 @@ pub trait QueryAccessors<CTX: QueryContext>: QueryConfig { // Don't use this method to compute query results, instead use the methods on TyCtxt fn compute_fn(tcx: CTX, key: &Self::Key) -> fn(CTX::DepContext, Self::Key) -> Self::Value; - fn hash_result(hcx: &mut StableHashingContext<'_>, result: &Self::Value) - -> Option<Fingerprint>; - fn handle_cycle_error(tcx: CTX, diag: DiagnosticBuilder<'_>) -> Self::Value; } @@ -95,7 +92,7 @@ pub trait QueryDescription<CTX: QueryContext>: QueryAccessors<CTX> { } pub(crate) trait QueryVtableExt<CTX: QueryContext, K, V> { - const VTABLE: QueryVtable<CTX, K, V>; + fn make_vtable(tcx: CTX, key: &K) -> QueryVtable<CTX, K, V>; } impl<CTX, Q> QueryVtableExt<CTX, Q::Key, Q::Value> for Q @@ -103,13 +100,16 @@ where CTX: QueryContext, Q: QueryDescription<CTX>, { - const VTABLE: QueryVtable<CTX, Q::Key, Q::Value> = QueryVtable { - anon: Q::ANON, - dep_kind: Q::DEP_KIND, - eval_always: Q::EVAL_ALWAYS, - hash_result: Q::hash_result, - handle_cycle_error: Q::handle_cycle_error, - cache_on_disk: Q::cache_on_disk, - try_load_from_disk: Q::try_load_from_disk, - }; + fn make_vtable(tcx: CTX, key: &Q::Key) -> QueryVtable<CTX, Q::Key, Q::Value> { + QueryVtable { + anon: Q::ANON, + dep_kind: Q::DEP_KIND, + eval_always: Q::EVAL_ALWAYS, + hash_result: Q::HASH_RESULT, + compute: Q::compute_fn(tcx, key), + handle_cycle_error: Q::handle_cycle_error, + cache_on_disk: Q::cache_on_disk, + try_load_from_disk: Q::try_load_from_disk, + } + } } diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index dffe7f3689f..e2b0a65ab77 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -14,7 +14,7 @@ pub use self::caches::{ mod config; pub use self::config::{QueryAccessors, QueryConfig, QueryDescription}; -use crate::dep_graph::{DepNode, DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; +use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; use rustc_data_structures::sync::Lock; use rustc_data_structures::thin_vec::ThinVec; @@ -122,12 +122,6 @@ pub trait QueryContext: HasDepContext { fn try_collect_active_jobs(&self) -> Option<QueryMap<Self::DepKind>>; - /// Load data from the on-disk cache. - fn try_load_from_on_disk_cache(&self, dep_node: &DepNode<Self::DepKind>); - - /// Try to force a dep node to execute and see if it's green. - fn try_force_from_dep_node(&self, dep_node: &DepNode<Self::DepKind>) -> bool; - /// Load side effects associated to the node in the previous session. fn load_side_effects(&self, prev_dep_node_index: SerializedDepNodeIndex) -> QuerySideEffects; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index d3c75635783..056611317dc 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -2,7 +2,7 @@ //! generate the actual methods on tcx which find and execute the provider, //! manage the caches, and so forth. -use crate::dep_graph::{DepContext, DepKind, DepNode, DepNodeIndex, DepNodeParams}; +use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams}; use crate::query::caches::QueryCache; use crate::query::config::{QueryDescription, QueryVtable, QueryVtableExt}; use crate::query::job::{ @@ -382,7 +382,6 @@ fn try_execute_query<CTX, C>( lookup: QueryLookup, dep_node: Option<DepNode<CTX::DepKind>>, query: &QueryVtable<CTX, C::Key, C::Value>, - compute: fn(CTX::DepContext, C::Key) -> C::Value, ) -> (C::Stored, Option<DepNodeIndex>) where C: QueryCache, @@ -398,7 +397,7 @@ where query.dep_kind, ) { TryGetJob::NotYetStarted(job) => { - let (result, dep_node_index) = execute_job(tcx, key, dep_node, query, job.id, compute); + let (result, dep_node_index) = execute_job(tcx, key, dep_node, query, job.id); let result = job.complete(cache, result, dep_node_index); (result, Some(dep_node_index)) } @@ -429,7 +428,6 @@ fn execute_job<CTX, K, V>( mut dep_node_opt: Option<DepNode<CTX::DepKind>>, query: &QueryVtable<CTX, K, V>, job_id: QueryJobId<CTX::DepKind>, - compute: fn(CTX::DepContext, K) -> V, ) -> (V, DepNodeIndex) where K: Clone + DepNodeParams<CTX::DepContext>, @@ -441,7 +439,7 @@ where // Fast path for when incr. comp. is off. if !dep_graph.is_fully_enabled() { let prof_timer = tcx.dep_context().profiler().query_provider(); - let result = tcx.start_query(job_id, None, || compute(*tcx.dep_context(), key)); + let result = tcx.start_query(job_id, None, || query.compute(*tcx.dep_context(), key)); let dep_node_index = dep_graph.next_virtual_depnode_index(); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); return (result, dep_node_index); @@ -455,7 +453,7 @@ where // The diagnostics for this query will be promoted to the current session during // `try_mark_green()`, so we can ignore them here. if let Some(ret) = tcx.start_query(job_id, None, || { - try_load_from_disk_and_cache_in_memory(tcx, &key, &dep_node, query, compute) + try_load_from_disk_and_cache_in_memory(tcx, &key, &dep_node, query) }) { return ret; } @@ -467,14 +465,14 @@ where let (result, dep_node_index) = tcx.start_query(job_id, Some(&diagnostics), || { if query.anon { return dep_graph.with_anon_task(*tcx.dep_context(), query.dep_kind, || { - compute(*tcx.dep_context(), key) + query.compute(*tcx.dep_context(), key) }); } // `to_dep_node` is expensive for some `DepKind`s. let dep_node = dep_node_opt.unwrap_or_else(|| query.to_dep_node(*tcx.dep_context(), &key)); - dep_graph.with_task(dep_node, *tcx.dep_context(), key, compute, query.hash_result) + dep_graph.with_task(dep_node, *tcx.dep_context(), key, query.compute, query.hash_result) }); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -498,7 +496,6 @@ fn try_load_from_disk_and_cache_in_memory<CTX, K, V>( key: &K, dep_node: &DepNode<CTX::DepKind>, query: &QueryVtable<CTX, K, V>, - compute: fn(CTX::DepContext, K) -> V, ) -> Option<(V, DepNodeIndex)> where K: Clone, @@ -520,14 +517,6 @@ where let result = query.try_load_from_disk(tcx, prev_dep_node_index); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - // We always expect to find a cached result for things that - // can be forced from `DepNode`. - debug_assert!( - !dep_node.kind.fingerprint_style().reconstructible() || result.is_some(), - "missing on-disk cache entry for {:?}", - dep_node - ); - if let Some(result) = result { // If `-Zincremental-verify-ich` is specified, re-hash results from // the cache and make sure that they have the expected fingerprint. @@ -537,6 +526,14 @@ where return Some((result, dep_node_index)); } + + // We always expect to find a cached result for things that + // can be forced from `DepNode`. + debug_assert!( + !tcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(), + "missing on-disk cache entry for {:?}", + dep_node + ); } // We could not load a result from the on-disk cache, so @@ -544,7 +541,7 @@ where let prof_timer = tcx.dep_context().profiler().query_provider(); // The dep-graph for this computation is already in-place. - let result = dep_graph.with_ignore(|| compute(*tcx.dep_context(), key.clone())); + let result = dep_graph.with_ignore(|| query.compute(*tcx.dep_context(), key.clone())); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -577,12 +574,12 @@ fn incremental_verify_ich<CTX, K, V: Debug>( ); debug!("BEGIN verify_ich({:?})", dep_node); - let mut hcx = tcx.create_stable_hashing_context(); - - let new_hash = query.hash_result(&mut hcx, result).unwrap_or(Fingerprint::ZERO); - debug!("END verify_ich({:?})", dep_node); - + let new_hash = query.hash_result.map_or(Fingerprint::ZERO, |f| { + let mut hcx = tcx.create_stable_hashing_context(); + f(&mut hcx, result) + }); let old_hash = tcx.dep_graph().prev_fingerprint_of(dep_node); + debug!("END verify_ich({:?})", dep_node); if Some(new_hash) != old_hash { let run_cmd = if let Some(crate_name) = &tcx.sess().opts.crate_name { @@ -665,41 +662,6 @@ where } } -#[inline(never)] -fn force_query_impl<CTX, C>( - tcx: CTX, - state: &QueryState<CTX::DepKind, C::Key>, - cache: &QueryCacheStore<C>, - key: C::Key, - dep_node: DepNode<CTX::DepKind>, - query: &QueryVtable<CTX, C::Key, C::Value>, - compute: fn(CTX::DepContext, C::Key) -> C::Value, -) -> bool -where - C: QueryCache, - C::Key: DepNodeParams<CTX::DepContext>, - CTX: QueryContext, -{ - debug_assert!(!query.anon); - - // We may be concurrently trying both execute and force a query. - // Ensure that only one of them runs the query. - let cached = cache.cache.lookup(cache, &key, |_, index| { - if unlikely!(tcx.dep_context().profiler().enabled()) { - tcx.dep_context().profiler().query_cache_hit(index.into()); - } - }); - - let lookup = match cached { - Ok(()) => return true, - Err(lookup) => lookup, - }; - - let _ = - try_execute_query(tcx, state, cache, DUMMY_SP, key, lookup, Some(dep_node), query, compute); - true -} - pub enum QueryMode { Get, Ensure, @@ -717,9 +679,9 @@ where Q::Key: DepNodeParams<CTX::DepContext>, CTX: QueryContext, { - let query = &Q::VTABLE; + let query = Q::make_vtable(tcx, &key); let dep_node = if let QueryMode::Ensure = mode { - let (must_run, dep_node) = ensure_must_run(tcx, &key, query); + let (must_run, dep_node) = ensure_must_run(tcx, &key, &query); if !must_run { return None; } @@ -729,7 +691,6 @@ where }; debug!("ty::query::get_query<{}>(key={:?}, span={:?})", Q::NAME, key, span); - let compute = Q::compute_fn(tcx, &key); let (result, dep_node_index) = try_execute_query( tcx, Q::query_state(tcx), @@ -738,8 +699,7 @@ where key, lookup, dep_node, - query, - compute, + &query, ); if let Some(dep_node_index) = dep_node_index { tcx.dep_context().dep_graph().read_index(dep_node_index) @@ -747,34 +707,29 @@ where Some(result) } -pub fn force_query<Q, CTX>(tcx: CTX, dep_node: &DepNode<CTX::DepKind>) -> bool +pub fn force_query<Q, CTX>(tcx: CTX, key: Q::Key, dep_node: DepNode<CTX::DepKind>) where Q: QueryDescription<CTX>, Q::Key: DepNodeParams<CTX::DepContext>, CTX: QueryContext, { - if Q::ANON { - return false; - } + assert!(!Q::ANON); - if !<Q::Key as DepNodeParams<CTX::DepContext>>::fingerprint_style().reconstructible() { - return false; - } + // We may be concurrently trying both execute and force a query. + // Ensure that only one of them runs the query. + let cache = Q::query_cache(tcx); + let cached = cache.cache.lookup(cache, &key, |_, index| { + if unlikely!(tcx.dep_context().profiler().enabled()) { + tcx.dep_context().profiler().query_cache_hit(index.into()); + } + }); - let Some(key) = - <Q::Key as DepNodeParams<CTX::DepContext>>::recover(*tcx.dep_context(), &dep_node) - else { - return false; + let lookup = match cached { + Ok(()) => return, + Err(lookup) => lookup, }; - let compute = Q::compute_fn(tcx, &key); - force_query_impl( - tcx, - Q::query_state(tcx), - Q::query_cache(tcx), - key, - *dep_node, - &Q::VTABLE, - compute, - ) + let query = Q::make_vtable(tcx, &key); + let state = Q::query_state(tcx); + try_execute_query(tcx, state, cache, DUMMY_SP, key, lookup, Some(dep_node), &query); } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b3d36b396c5..059b5dc5175 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1283,7 +1283,7 @@ options! { "specify the events recorded by the self profiler; for example: `-Z self-profile-events=default,query-keys` all options: none, all, default, generic-activity, query-provider, query-cache-hit - query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm"), + query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"), share_generics: Option<bool> = (None, parse_opt_bool, [TRACKED], "make the current crate share its generic instantiations"), show_span: Option<String> = (None, parse_opt_string, [TRACKED], diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ae148624a90..c25bd9bfa80 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -355,7 +355,6 @@ symbols! { await_macro, bang, begin_panic, - begin_panic_fmt, bench, bin, bind_by_move_pattern_guards, diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 1193d10d6a7..8edb7069fc4 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -80,9 +80,8 @@ pub fn is_const_evaluatable<'cx, 'tcx>( Concrete, } let mut failure_kind = FailureKind::Concrete; - walk_abstract_const::<!, _>(tcx, ct, |node| match node.root() { + walk_abstract_const::<!, _>(tcx, ct, |node| match node.root(tcx) { Node::Leaf(leaf) => { - let leaf = leaf.subst(tcx, ct.substs); if leaf.has_infer_types_or_consts() { failure_kind = FailureKind::MentionsInfer; } else if leaf.definitely_has_param_types_or_consts(tcx) { @@ -92,7 +91,6 @@ pub fn is_const_evaluatable<'cx, 'tcx>( ControlFlow::CONTINUE } Node::Cast(_, _, ty) => { - let ty = ty.subst(tcx, ct.substs); if ty.has_infer_types_or_consts() { failure_kind = FailureKind::MentionsInfer; } else if ty.definitely_has_param_types_or_consts(tcx) { @@ -187,8 +185,8 @@ pub fn is_const_evaluatable<'cx, 'tcx>( pub struct AbstractConst<'tcx> { // FIXME: Consider adding something like `IndexSlice` // and use this here. - pub inner: &'tcx [Node<'tcx>], - pub substs: SubstsRef<'tcx>, + inner: &'tcx [Node<'tcx>], + substs: SubstsRef<'tcx>, } impl<'tcx> AbstractConst<'tcx> { @@ -218,8 +216,14 @@ impl<'tcx> AbstractConst<'tcx> { } #[inline] - pub fn root(self) -> Node<'tcx> { - self.inner.last().copied().unwrap() + pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> { + let node = self.inner.last().copied().unwrap(); + match node { + Node::Leaf(leaf) => Node::Leaf(leaf.subst(tcx, self.substs)), + Node::Cast(kind, operand, ty) => Node::Cast(kind, operand, ty.subst(tcx, self.substs)), + // Don't perform substitution on the following as they can't directly contain generic params + Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node, + } } } @@ -542,7 +546,7 @@ where f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, ) -> ControlFlow<R> { f(ct)?; - let root = ct.root(); + let root = ct.root(tcx); match root { Node::Leaf(_) => ControlFlow::CONTINUE, Node::Binop(_, l, r) => { @@ -570,16 +574,14 @@ pub(super) fn try_unify<'tcx>( // We substitute generics repeatedly to allow AbstractConsts to unify where a // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] - while let Node::Leaf(a_ct) = a.root() { - let a_ct = a_ct.subst(tcx, a.substs); + while let Node::Leaf(a_ct) = a.root(tcx) { match AbstractConst::from_const(tcx, a_ct) { Ok(Some(a_act)) => a = a_act, Ok(None) => break, Err(_) => return true, } } - while let Node::Leaf(b_ct) = b.root() { - let b_ct = b_ct.subst(tcx, b.substs); + while let Node::Leaf(b_ct) = b.root(tcx) { match AbstractConst::from_const(tcx, b_ct) { Ok(Some(b_act)) => b = b_act, Ok(None) => break, @@ -587,10 +589,8 @@ pub(super) fn try_unify<'tcx>( } } - match (a.root(), b.root()) { + match (a.root(tcx), b.root(tcx)) { (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { - let a_ct = a_ct.subst(tcx, a.substs); - let b_ct = b_ct.subst(tcx, b.substs); if a_ct.ty != b_ct.ty { return false; } diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index ad9fd0ca62c..afc546540d2 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -837,14 +837,13 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>( // constants which are not considered const evaluatable. use rustc_middle::thir::abstract_const::Node; if let Ok(Some(ct)) = AbstractConst::new(self.tcx, uv.shrink()) { - const_evaluatable::walk_abstract_const(self.tcx, ct, |node| match node.root() { - Node::Leaf(leaf) => { - let leaf = leaf.subst(self.tcx, ct.substs); - self.visit_const(leaf) - } - Node::Cast(_, _, ty) => self.visit_ty(ty), - Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => { - ControlFlow::CONTINUE + const_evaluatable::walk_abstract_const(self.tcx, ct, |node| { + match node.root(self.tcx) { + Node::Leaf(leaf) => self.visit_const(leaf), + Node::Cast(_, _, ty) => self.visit_ty(ty), + Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => { + ControlFlow::CONTINUE + } } }) } else { diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index ac8bab0cf36..a398e847b93 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -17,6 +17,7 @@ pub enum NonStructuralMatchTy<'tcx> { Dynamic, Foreign, Opaque, + Closure, Generator, Projection, } @@ -154,6 +155,9 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { ty::Projection(..) => { return ControlFlow::Break(NonStructuralMatchTy::Projection); } + ty::Closure(..) => { + return ControlFlow::Break(NonStructuralMatchTy::Closure); + } ty::Generator(..) | ty::GeneratorWitness(..) => { return ControlFlow::Break(NonStructuralMatchTy::Generator); } @@ -197,7 +201,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { // First check all contained types and then tell the caller to continue searching. return ty.super_visit_with(self); } - ty::Closure(..) | ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { + ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { bug!("unexpected type during structural-match checking: {:?}", ty); } ty::Error(_) => { diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 3d3b2743700..bc77c94809e 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -1,7 +1,6 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::hir::map as hir_map; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{ self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt, WithConstness, @@ -478,11 +477,11 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync { let node = tcx.hir().get(hir_id); - let fn_like = hir_map::blocks::FnLikeNode::from_node(node).unwrap_or_else(|| { + let fn_kind = node.fn_kind().unwrap_or_else(|| { bug!("asyncness: expected fn-like node but got `{:?}`", def_id); }); - fn_like.asyncness() + fn_kind.asyncness() } /// Don't call this directly: use ``tcx.conservative_is_privately_uninhabited`` instead. diff --git a/compiler/rustc_typeck/src/bounds.rs b/compiler/rustc_typeck/src/bounds.rs index ff04e07acc4..24474e163b9 100644 --- a/compiler/rustc_typeck/src/bounds.rs +++ b/compiler/rustc_typeck/src/bounds.rs @@ -64,16 +64,16 @@ impl<'tcx> Bounds<'tcx> { }) }); - self.region_bounds - .iter() - .map(|&(region_bound, span)| { + sized_predicate + .into_iter() + .chain(self.region_bounds.iter().map(|&(region_bound, span)| { ( region_bound .map_bound(|region_bound| ty::OutlivesPredicate(param_ty, region_bound)) .to_predicate(tcx), span, ) - }) + })) .chain(self.trait_bounds.iter().map(|&(bound_trait_ref, span, constness)| { let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx); (predicate, span) @@ -83,7 +83,6 @@ impl<'tcx> Bounds<'tcx> { .iter() .map(|&(projection, span)| (projection.to_predicate(tcx), span)), ) - .chain(sized_predicate.into_iter()) .collect() } } diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index caad28ff2b2..9c7b0b2cacb 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -883,8 +883,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.get_diagnostic_item(sym::unwind_safe_trait), self.tcx.get_diagnostic_item(sym::ref_unwind_safe_trait), ]; - let auto_traits = - vec!["`Clone`", "`Sync`", "`Send`", "`Unpin`", "`UnwindSafe`", "`RefUnwindSafe`"]; + const AUTO_TRAITS: [&str; 6] = + ["`Clone`", "`Sync`", "`Send`", "`Unpin`", "`UnwindSafe`", "`RefUnwindSafe`"]; let root_var_min_capture_list = min_captures.and_then(|m| m.get(&var_hir_id))?; @@ -957,7 +957,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // by the root variable but not by the capture for (idx, _) in obligations_should_hold.iter().enumerate() { if !obligations_holds_for_capture[idx] && obligations_should_hold[idx] { - capture_problems.insert(auto_traits[idx]); + capture_problems.insert(AUTO_TRAITS[idx]); } } @@ -1074,7 +1074,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, ) -> (Vec<MigrationDiagnosticInfo>, String) { let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) else { - return (Vec::new(), format!("")); + return (Vec::new(), String::new()); }; let mut need_migrations = Vec::new(); diff --git a/config.toml.example b/config.toml.example index 61e57eee782..2128fdea2eb 100644 --- a/config.toml.example +++ b/config.toml.example @@ -68,6 +68,12 @@ changelog-seen = 2 # Indicates whether the LLVM assertions are enabled or not #assertions = false +# Indicates whether the LLVM testsuite is enabled in the build or not. Does +# not execute the tests as part of the build as part of x.py build et al, +# just makes it possible to do `ninja check-llvm` in the staged LLVM build +# directory when doing LLVM development as part of Rust development. +#tests = false + # Indicates whether the LLVM plugin is enabled or not #plugins = false diff --git a/library/alloc/src/collections/binary_heap.rs b/library/alloc/src/collections/binary_heap.rs index 9bded6c0f1c..d7620c68f2c 100644 --- a/library/alloc/src/collections/binary_heap.rs +++ b/library/alloc/src/collections/binary_heap.rs @@ -162,9 +162,9 @@ use super::SpecExtend; /// item's ordering relative to any other item, as determined by the [`Ord`] /// trait, changes while it is in the heap. This is normally only possible /// through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. The -/// behavior resulting from such a logic error is not specified, but will -/// not result in undefined behavior. This could include panics, incorrect -/// results, aborts, memory leaks, and non-termination. +/// behavior resulting from such a logic error is not specified (it +/// could include panics, incorrect results, aborts, memory leaks, or +/// non-termination) but will not be undefined behavior. /// /// # Examples /// diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index fa86e611565..07920a6dba6 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -61,9 +61,9 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; /// It is a logic error for a key to be modified in such a way that the key's ordering relative to /// any other key, as determined by the [`Ord`] trait, changes while it is in the map. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// The behavior resulting from such a logic error is not specified, but will not result in -/// undefined behavior. This could include panics, incorrect results, aborts, memory leaks, and -/// non-termination. +/// The behavior resulting from such a logic error is not specified (it could include panics, +/// incorrect results, aborts, memory leaks, or non-termination) but will not be undefined +/// behavior. /// /// [B-Tree]: https://en.wikipedia.org/wiki/B-tree /// [`Cell`]: core::cell::Cell diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index d732f65b0d0..237e0107f24 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -23,9 +23,9 @@ use super::Recover; /// It is a logic error for an item to be modified in such a way that the item's ordering relative /// to any other item, as determined by the [`Ord`] trait, changes while it is in the set. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// The behavior resulting from such a logic error is not specified, but will not result in -/// undefined behavior. This could include panics, incorrect results, aborts, memory leaks, and -/// non-termination. +/// The behavior resulting from such a logic error is not specified (it could include panics, +/// incorrect results, aborts, memory leaks, or non-termination) but will not be undefined +/// behavior. /// /// [`Ord`]: core::cmp::Ord /// [`Cell`]: core::cell::Cell diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index c890ff4ac5e..de607c8fdab 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -543,9 +543,9 @@ impl<T, A: Allocator> VecDeque<T, A> { /// ``` #[unstable(feature = "allocator_api", issue = "32838")] pub fn with_capacity_in(capacity: usize, alloc: A) -> VecDeque<T, A> { + assert!(capacity < 1_usize << usize::BITS - 1, "capacity overflow"); // +1 since the ringbuffer always leaves one space empty let cap = cmp::max(capacity + 1, MINIMUM_CAPACITY + 1).next_power_of_two(); - assert!(cap > capacity, "capacity overflow"); VecDeque { tail: 0, head: 0, buf: RawVec::with_capacity_in(cap, alloc) } } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 635708fd4cf..285d7755c06 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -80,87 +80,90 @@ )] #![no_std] #![needs_allocator] +// +// Lints: +#![deny(unsafe_op_in_unsafe_fn)] #![warn(deprecated_in_future)] -#![warn(missing_docs)] #![warn(missing_debug_implementations)] +#![warn(missing_docs)] #![allow(explicit_outlives_requirements)] -#![deny(unsafe_op_in_unsafe_fn)] -#![feature(rustc_allow_const_fn_unstable)] -#![cfg_attr(not(test), feature(generator_trait))] -#![cfg_attr(test, feature(test))] -#![cfg_attr(test, feature(new_uninit))] +// +// Library features: +#![feature(alloc_layout_extra)] #![feature(allocator_api)] #![feature(array_chunks)] #![feature(array_methods)] #![feature(array_windows)] -#![feature(allow_internal_unstable)] -#![feature(arbitrary_self_types)] #![feature(async_stream)] -#![feature(box_patterns)] -#![feature(box_syntax)] -#![feature(cfg_sanitize)] -#![feature(cfg_target_has_atomic)] #![feature(coerce_unsized)] #![cfg_attr(not(no_global_oom_handling), feature(const_btree_new))] -#![feature(const_fn_trait_bound)] -#![feature(cow_is_borrowed)] #![feature(const_cow_is_borrowed)] -#![feature(const_trait_impl)] -#![feature(destructuring_assignment)] -#![feature(dispatch_from_dyn)] #![feature(core_intrinsics)] -#![feature(dropck_eyepatch)] +#![feature(dispatch_from_dyn)] #![feature(exact_size_is_empty)] -#![feature(exclusive_range_pattern)] #![feature(extend_one)] #![feature(fmt_internals)] #![feature(fn_traits)] -#![feature(fundamental)] #![feature(inplace_iteration)] -// Technically, this is a bug in rustdoc: rustdoc sees the documentation on `#[lang = slice_alloc]` -// blocks is for `&[T]`, which also has documentation using this feature in `core`, and gets mad -// that the feature-gate isn't enabled. Ideally, it wouldn't check for the feature gate for docs -// from other crates, but since this can only appear for lang items, it doesn't seem worth fixing. -#![feature(intra_doc_pointers)] #![feature(iter_advance_by)] #![feature(iter_zip)] -#![feature(lang_items)] #![feature(layout_for_ptr)] -#![feature(negative_impls)] -#![feature(never_type)] -#![feature(nll)] +#![feature(maybe_uninit_extra)] +#![feature(maybe_uninit_slice)] +#![cfg_attr(test, feature(new_uninit))] #![feature(nonnull_slice_from_raw_parts)] -#![feature(auto_traits)] #![feature(option_result_unwrap_unchecked)] #![feature(pattern)] #![feature(ptr_internals)] -#![feature(rustc_attrs)] #![feature(receiver_trait)] -#![feature(min_specialization)] #![feature(set_ptr_value)] +#![feature(slice_group_by)] #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] #![feature(slice_range)] -#![feature(staged_api)] #![feature(str_internals)] #![feature(trusted_len)] -#![feature(unboxed_closures)] +#![feature(trusted_random_access)] +#![feature(try_trait_v2)] #![feature(unicode_internals)] #![feature(unsize)] -#![feature(unsized_fn_params)] +// +// Language features: #![feature(allocator_internals)] -#![feature(slice_partition_dedup)] -#![feature(maybe_uninit_extra, maybe_uninit_slice, maybe_uninit_uninit_array)] -#![feature(alloc_layout_extra)] -#![feature(trusted_random_access)] -#![feature(try_trait_v2)] +#![feature(allow_internal_unstable)] #![feature(associated_type_bounds)] -#![feature(slice_group_by)] -#![feature(decl_macro)] +#![feature(box_syntax)] +#![feature(cfg_sanitize)] +#![feature(cfg_target_has_atomic)] +#![feature(const_fn_trait_bound)] +#![feature(const_trait_impl)] +#![feature(destructuring_assignment)] +#![feature(dropck_eyepatch)] +#![feature(exclusive_range_pattern)] +#![feature(fundamental)] +#![cfg_attr(not(test), feature(generator_trait))] +#![feature(lang_items)] +#![feature(min_specialization)] +#![feature(negative_impls)] +#![feature(never_type)] +#![feature(nll)] // Not necessary, but here to test the `nll` feature. +#![feature(rustc_allow_const_fn_unstable)] +#![feature(rustc_attrs)] +#![feature(staged_api)] +#![cfg_attr(test, feature(test))] +#![feature(unboxed_closures)] +#![feature(unsized_fn_params)] +// +// Rustdoc features: #![feature(doc_cfg)] #![cfg_attr(not(bootstrap), feature(doc_cfg_hide))] -// Allow testing this library +// Technically, this is a bug in rustdoc: rustdoc sees the documentation on `#[lang = slice_alloc]` +// blocks is for `&[T]`, which also has documentation using this feature in `core`, and gets mad +// that the feature-gate isn't enabled. Ideally, it wouldn't check for the feature gate for docs +// from other crates, but since this can only appear for lang items, it doesn't seem worth fixing. +#![feature(intra_doc_pointers)] +// Allow testing this library #[cfg(test)] #[macro_use] extern crate std; diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 20a16869cb3..d52c78eedf3 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1305,10 +1305,11 @@ impl<T, A: Allocator> Vec<T, A> { // We replace self[index] with the last element. Note that if the // bounds check above succeeds there must be a last element (which // can be self[index] itself). - let last = ptr::read(self.as_ptr().add(len - 1)); - let hole = self.as_mut_ptr().add(index); + let value = ptr::read(self.as_ptr().add(index)); + let base_ptr = self.as_mut_ptr(); + ptr::copy(base_ptr.add(len - 1), base_ptr.add(index), 1); self.set_len(len - 1); - ptr::replace(hole, last) + value } } diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 8d5c0510404..b27c36baf37 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -125,7 +125,8 @@ impl TryFromSliceError { } #[stable(feature = "try_from_slice_error", since = "1.36.0")] -impl From<Infallible> for TryFromSliceError { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const From<Infallible> for TryFromSliceError { fn from(x: Infallible) -> TryFromSliceError { match x {} } diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 2ca077a98f8..ed464700cd3 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -308,7 +308,8 @@ impl<T: Ord + Copy> Ord for Cell<T> { } #[stable(feature = "cell_from", since = "1.12.0")] -impl<T> From<T> for Cell<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T> const From<T> for Cell<T> { fn from(t: T) -> Cell<T> { Cell::new(t) } @@ -1236,7 +1237,8 @@ impl<T: ?Sized + Ord> Ord for RefCell<T> { } #[stable(feature = "cell_from", since = "1.12.0")] -impl<T> From<T> for RefCell<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T> const From<T> for RefCell<T> { fn from(t: T) -> RefCell<T> { RefCell::new(t) } @@ -1976,7 +1978,8 @@ impl<T: Default> Default for UnsafeCell<T> { } #[stable(feature = "cell_from", since = "1.12.0")] -impl<T> From<T> for UnsafeCell<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T> const From<T> for UnsafeCell<T> { fn from(t: T) -> UnsafeCell<T> { UnsafeCell::new(t) } diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 72921414fb3..8fc6b1af924 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -97,7 +97,8 @@ pub unsafe fn from_u32_unchecked(i: u32) -> char { } #[stable(feature = "char_convert", since = "1.13.0")] -impl From<char> for u32 { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const From<char> for u32 { /// Converts a [`char`] into a [`u32`]. /// /// # Examples @@ -116,7 +117,8 @@ impl From<char> for u32 { } #[stable(feature = "more_char_conversions", since = "1.51.0")] -impl From<char> for u64 { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const From<char> for u64 { /// Converts a [`char`] into a [`u64`]. /// /// # Examples @@ -137,7 +139,8 @@ impl From<char> for u64 { } #[stable(feature = "more_char_conversions", since = "1.51.0")] -impl From<char> for u128 { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const From<char> for u128 { /// Converts a [`char`] into a [`u128`]. /// /// # Examples @@ -176,7 +179,8 @@ impl From<char> for u128 { /// for a superset of Windows-1252 that fills the remaining blanks with corresponding /// C0 and C1 control codes. #[stable(feature = "char_convert", since = "1.13.0")] -impl From<u8> for char { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const From<u8> for char { /// Converts a [`u8`] into a [`char`]. /// /// # Examples diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index fb8305273a8..5aa53deee34 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -545,7 +545,8 @@ where // From (and thus Into) is reflexive #[stable(feature = "rust1", since = "1.0.0")] -impl<T> From<T> for T { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T> const From<T> for T { fn from(t: T) -> T { t } @@ -560,7 +561,8 @@ impl<T> From<T> for T { #[allow(unused_attributes)] // FIXME(#58633): do a principled fix instead. #[rustc_reservation_impl = "permitting this impl would forbid us from adding \ `impl<T> From<!> for T` later; see rust-lang/rust#64715 for details"] -impl<T> From<!> for T { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T> const From<!> for T { fn from(t: !) -> T { t } @@ -726,7 +728,8 @@ impl Ord for Infallible { } #[stable(feature = "convert_infallible", since = "1.34.0")] -impl From<!> for Infallible { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const From<!> for Infallible { fn from(x: !) -> Self { x } diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 75ef873abc9..2b6ea90bf04 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -390,7 +390,8 @@ use crate::num::NonZeroUsize; macro_rules! nzint_impl_from { ($Small: ty, $Large: ty, #[$attr:meta], $doc: expr) => { #[$attr] - impl From<$Small> for $Large { + #[rustc_const_unstable(feature = "const_num_from_num", issue = "87852")] + impl const From<$Small> for $Large { // Rustdocs on the impl block show a "[+] show undocumented items" toggle. // Rustdocs on functions do not. #[doc = $doc] @@ -398,7 +399,7 @@ macro_rules! nzint_impl_from { fn from(small: $Small) -> Self { // SAFETY: input type guarantees the value is non-zero unsafe { - Self::new_unchecked(small.get().into()) + Self::new_unchecked(From::from(small.get())) } } } diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index ba65f0fadbd..89d5fac30d3 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -3,6 +3,26 @@ use crate::mem::MaybeUninit; use crate::num::flt2dec; use crate::num::fmt as numfmt; +#[doc(hidden)] +trait GeneralFormat: PartialOrd { + /// Determines if a value should use exponential based on its magnitude, given the precondition + /// that it will not be rounded any further before it is displayed. + fn already_rounded_value_should_use_exponential(&self) -> bool; +} + +macro_rules! impl_general_format { + ($($t:ident)*) => { + $(impl GeneralFormat for $t { + fn already_rounded_value_should_use_exponential(&self) -> bool { + let abs = $t::abs_private(*self); + (abs != 0.0 && abs < 1e-4) || abs >= 1e+16 + } + })* + } +} + +impl_general_format! { f32 f64 } + // Don't inline this so callers don't use the stack space this function // requires unless they have to. #[inline(never)] @@ -54,8 +74,7 @@ where fmt.pad_formatted_parts(&formatted) } -// Common code of floating point Debug and Display. -fn float_to_decimal_common<T>(fmt: &mut Formatter<'_>, num: &T, min_precision: usize) -> Result +fn float_to_decimal_display<T>(fmt: &mut Formatter<'_>, num: &T) -> Result where T: flt2dec::DecodableFloat, { @@ -68,6 +87,7 @@ where if let Some(precision) = fmt.precision { float_to_decimal_common_exact(fmt, num, sign, precision) } else { + let min_precision = 0; float_to_decimal_common_shortest(fmt, num, sign, min_precision) } } @@ -145,19 +165,44 @@ where } } +fn float_to_general_debug<T>(fmt: &mut Formatter<'_>, num: &T) -> Result +where + T: flt2dec::DecodableFloat + GeneralFormat, +{ + let force_sign = fmt.sign_plus(); + let sign = match force_sign { + false => flt2dec::Sign::Minus, + true => flt2dec::Sign::MinusPlus, + }; + + if let Some(precision) = fmt.precision { + // this behavior of {:.PREC?} predates exponential formatting for {:?} + float_to_decimal_common_exact(fmt, num, sign, precision) + } else { + // since there is no precision, there will be no rounding + if num.already_rounded_value_should_use_exponential() { + let upper = false; + float_to_exponential_common_shortest(fmt, num, sign, upper) + } else { + let min_precision = 1; + float_to_decimal_common_shortest(fmt, num, sign, min_precision) + } + } +} + macro_rules! floating { ($ty:ident) => { #[stable(feature = "rust1", since = "1.0.0")] impl Debug for $ty { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { - float_to_decimal_common(fmt, self, 1) + float_to_general_debug(fmt, self) } } #[stable(feature = "rust1", since = "1.0.0")] impl Display for $ty { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { - float_to_decimal_common(fmt, self, 0) + float_to_decimal_display(fmt, self) } } diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs index 2b7287a4133..4b89bc36326 100644 --- a/library/core/src/iter/adapters/zip.rs +++ b/library/core/src/iter/adapters/zip.rs @@ -427,13 +427,9 @@ where } } +// Since SourceIter forwards the left hand side we do the same here #[unstable(issue = "none", feature = "inplace_iteration")] -// Limited to Item: Copy since interaction between Zip's use of TrustedRandomAccess -// and Drop implementation of the source is unclear. -// -// An additional method returning the number of times the source has been logically advanced -// (without calling next()) would be needed to properly drop the remainder of the source. -unsafe impl<A: InPlaceIterable, B: Iterator> InPlaceIterable for Zip<A, B> where A::Item: Copy {} +unsafe impl<A: InPlaceIterable, B: Iterator> InPlaceIterable for Zip<A, B> {} #[stable(feature = "rust1", since = "1.0.0")] impl<A: Debug, B: Debug> Debug for Zip<A, B> { diff --git a/library/core/src/lazy.rs b/library/core/src/lazy.rs index d109141216a..2b8a5f3cbf3 100644 --- a/library/core/src/lazy.rs +++ b/library/core/src/lazy.rs @@ -74,7 +74,7 @@ impl<T: PartialEq> PartialEq for OnceCell<T> { impl<T: Eq> Eq for OnceCell<T> {} #[unstable(feature = "once_cell", issue = "74465")] -impl<T> From<T> for OnceCell<T> { +impl<T> const From<T> for OnceCell<T> { fn from(value: T) -> Self { OnceCell { inner: UnsafeCell::new(Some(value)) } } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 13b80c05dbb..58a170401e7 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -115,6 +115,7 @@ #![feature(const_likely)] #![feature(const_maybe_uninit_as_ptr)] #![feature(const_maybe_uninit_assume_init)] +#![feature(const_num_from_num)] #![feature(const_option)] #![feature(const_pin)] #![feature(const_replace)] diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index cdeba9c0792..2af61a07482 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -29,14 +29,15 @@ impl fmt::Display for TryFromIntError { } #[stable(feature = "try_from", since = "1.34.0")] -impl From<Infallible> for TryFromIntError { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const From<Infallible> for TryFromIntError { fn from(x: Infallible) -> TryFromIntError { match x {} } } #[unstable(feature = "never_type", issue = "35121")] -impl From<!> for TryFromIntError { +impl const From<!> for TryFromIntError { fn from(never: !) -> TryFromIntError { // Match rather than coerce to make sure that code like // `From<Infallible> for TryFromIntError` above will keep working diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index ad8106df198..83a922ae348 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -449,7 +449,7 @@ impl f32 { // private use internally. #[inline] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] - const fn abs_private(self) -> f32 { + pub(crate) const fn abs_private(self) -> f32 { f32::from_bits(self.to_bits() & 0x7fff_ffff) } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 6a48101e04f..4267260eea3 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -448,7 +448,7 @@ impl f64 { // private use internally. #[inline] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] - const fn abs_private(self) -> f64 { + pub(crate) const fn abs_private(self) -> f64 { f64::from_bits(self.to_bits() & 0x7fff_ffff_ffff_ffff) } diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 0bdc9330137..5ef2558a5fe 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1055,8 +1055,6 @@ macro_rules! int_impl { /// Basic usage: /// /// ``` - /// #![feature(saturating_div)] - /// #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".saturating_div(2), 2);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_div(-1), ", stringify!($SelfT), "::MIN + 1);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_div(-1), ", stringify!($SelfT), "::MAX);")] @@ -1064,13 +1062,10 @@ macro_rules! int_impl { /// ``` /// /// ```should_panic - /// #![feature(saturating_div)] - /// #[doc = concat!("let _ = 1", stringify!($SelfT), ".saturating_div(0);")] /// /// ``` - #[unstable(feature = "saturating_div", issue = "87920")] - #[rustc_const_unstable(feature = "saturating_div", issue = "87920")] + #[stable(feature = "saturating_div", since = "1.58.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 89fd9fbaf45..9b1a4de5d80 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -82,7 +82,8 @@ macro_rules! nonzero_integers { } #[stable(feature = "from_nonzero", since = "1.31.0")] - impl From<$Ty> for $Int { + #[rustc_const_unstable(feature = "const_num_from_num", issue = "87852")] + impl const From<$Ty> for $Int { #[doc = concat!("Converts a `", stringify!($Ty), "` into an `", stringify!($Int), "`")] #[inline] fn from(nonzero: $Ty) -> Self { diff --git a/library/core/src/num/saturating.rs b/library/core/src/num/saturating.rs index c764f420e27..ba81f3f9fd6 100644 --- a/library/core/src/num/saturating.rs +++ b/library/core/src/num/saturating.rs @@ -273,7 +273,7 @@ macro_rules! saturating_impl { /// Basic usage: /// /// ``` - /// #![feature(saturating_int_impl, saturating_div)] + /// #![feature(saturating_int_impl)] /// use std::num::Saturating; /// #[doc = concat!("assert_eq!(Saturating(2", stringify!($t), "), Saturating(5", stringify!($t), ") / Saturating(2));")] @@ -282,7 +282,7 @@ macro_rules! saturating_impl { /// ``` /// /// ```should_panic - /// #![feature(saturating_int_impl, saturating_div)] + /// #![feature(saturating_int_impl)] /// use std::num::Saturating; /// #[doc = concat!("let _ = Saturating(0", stringify!($t), ") / Saturating(0);")] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index c3b2ecdb30f..507ff516a8f 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1120,20 +1120,15 @@ macro_rules! uint_impl { /// Basic usage: /// /// ``` - /// #![feature(saturating_div)] - /// #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".saturating_div(2), 2);")] /// /// ``` /// /// ```should_panic - /// #![feature(saturating_div)] - /// #[doc = concat!("let _ = 1", stringify!($SelfT), ".saturating_div(0);")] /// /// ``` - #[unstable(feature = "saturating_div", issue = "87920")] - #[rustc_const_unstable(feature = "saturating_div", issue = "87920")] + #[stable(feature = "saturating_div", since = "1.58.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 88505832158..f4ce7d1dfb3 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1723,7 +1723,8 @@ impl<'a, T> IntoIterator for &'a mut Option<T> { } #[stable(since = "1.12.0", feature = "option_from")] -impl<T> From<T> for Option<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T> const From<T> for Option<T> { /// Moves `val` into a new [`Some`]. /// /// # Examples @@ -1739,7 +1740,8 @@ impl<T> From<T> for Option<T> { } #[stable(feature = "option_ref_from_ref_option", since = "1.30.0")] -impl<'a, T> From<&'a Option<T>> for Option<&'a T> { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<'a, T> const From<&'a Option<T>> for Option<&'a T> { /// Converts from `&Option<T>` to `Option<&T>`. /// /// # Examples @@ -1766,7 +1768,8 @@ impl<'a, T> From<&'a Option<T>> for Option<&'a T> { } #[stable(feature = "option_ref_from_ref_option", since = "1.30.0")] -impl<'a, T> From<&'a mut Option<T>> for Option<&'a mut T> { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<'a, T> const From<&'a mut Option<T>> for Option<&'a mut T> { /// Converts from `&mut Option<T>` to `Option<&mut T>` /// /// # Examples @@ -2052,7 +2055,7 @@ impl<T> ops::Try for Option<T> { } #[unstable(feature = "try_trait_v2", issue = "84277")] -impl<T> ops::FromResidual for Option<T> { +impl<T> const ops::FromResidual for Option<T> { #[inline] fn from_residual(residual: Option<convert::Infallible>) -> Self { match residual { diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index a52a0022e5d..649bc3e44ad 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -121,7 +121,7 @@ impl<'a> PanicInfo<'a> { #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn location(&self) -> Option<&Location<'_>> { // NOTE: If this is changed to sometimes return None, - // deal with that case in std::panicking::default_hook and std::panicking::begin_panic_fmt. + // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. Some(&self.location) } } diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 6d3ec6ae861..a12447acf7e 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -76,8 +76,15 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { panic!("index out of bounds: the len is {} but the index is {}", len, index) } -/// The underlying implementation of libcore's `panic!` macro when formatting is used. +/// The entry point for panicking with a formatted message. +/// +/// This is designed to reduce the amount of code required at the call +/// site as much as possible (so that `panic!()` has as low an impact +/// on (e.g.) the inlining of other functions as possible), by moving +/// the actual formatting into this shared place. #[cold] +// If panic_immediate_abort, inline the abort call, +// otherwise avoid inlining because of it is cold path. #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index ee93f00a7fb..8bae66ca007 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -698,7 +698,8 @@ impl<T: ?Sized> hash::Hash for NonNull<T> { } #[unstable(feature = "ptr_internals", issue = "none")] -impl<T: ?Sized> From<Unique<T>> for NonNull<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T: ?Sized> const From<Unique<T>> for NonNull<T> { #[inline] fn from(unique: Unique<T>) -> Self { // SAFETY: A Unique pointer cannot be null, so the conditions for @@ -708,7 +709,8 @@ impl<T: ?Sized> From<Unique<T>> for NonNull<T> { } #[stable(feature = "nonnull", since = "1.25.0")] -impl<T: ?Sized> From<&mut T> for NonNull<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T: ?Sized> const From<&mut T> for NonNull<T> { #[inline] fn from(reference: &mut T) -> Self { // SAFETY: A mutable reference cannot be null. @@ -717,7 +719,8 @@ impl<T: ?Sized> From<&mut T> for NonNull<T> { } #[stable(feature = "nonnull", since = "1.25.0")] -impl<T: ?Sized> From<&T> for NonNull<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T: ?Sized> const From<&T> for NonNull<T> { #[inline] fn from(reference: &T) -> Self { // SAFETY: A reference cannot be null, so the conditions for diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index 5baceefb504..f6eb48f2967 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -176,7 +176,7 @@ impl<T: ?Sized> fmt::Pointer for Unique<T> { } #[unstable(feature = "ptr_internals", issue = "none")] -impl<T: ?Sized> From<&mut T> for Unique<T> { +impl<T: ?Sized> const From<&mut T> for Unique<T> { #[inline] fn from(reference: &mut T) -> Self { // SAFETY: A mutable reference cannot be null diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 1247f330875..1dd3b2d8e3c 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1273,7 +1273,8 @@ impl<T> AtomicPtr<T> { #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "atomic_bool_from", since = "1.24.0")] -impl From<bool> for AtomicBool { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const From<bool> for AtomicBool { /// Converts a `bool` into an `AtomicBool`. /// /// # Examples @@ -1291,7 +1292,8 @@ impl From<bool> for AtomicBool { #[cfg(target_has_atomic_load_store = "ptr")] #[stable(feature = "atomic_from", since = "1.23.0")] -impl<T> From<*mut T> for AtomicPtr<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T> const From<*mut T> for AtomicPtr<T> { #[inline] fn from(p: *mut T) -> Self { Self::new(p) @@ -1363,7 +1365,8 @@ macro_rules! atomic_int { } #[$stable_from] - impl From<$int_type> for $atomic_type { + #[rustc_const_unstable(feature = "const_num_from_num", issue = "87852")] + impl const From<$int_type> for $atomic_type { #[doc = concat!("Converts an `", stringify!($int_type), "` into an `", stringify!($atomic_type), "`.")] #[inline] fn from(v: $int_type) -> Self { Self::new(v) } diff --git a/library/core/src/task/poll.rs b/library/core/src/task/poll.rs index 80e1458dc94..72a030617ad 100644 --- a/library/core/src/task/poll.rs +++ b/library/core/src/task/poll.rs @@ -241,7 +241,8 @@ impl<T, E> Poll<Option<Result<T, E>>> { } #[stable(feature = "futures_api", since = "1.36.0")] -impl<T> From<T> for Poll<T> { +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl<T> const From<T> for Poll<T> { /// Convert to a `Ready` variant. /// /// # Example diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 5a74f39e8bc..7114f2d652e 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -756,7 +756,7 @@ impl Duration { } else if nanos >= MAX_NANOS_F64 { Err(FromSecsError { kind: FromSecsErrorKind::Overflow }) } else if nanos < 0.0 { - Err(FromSecsError { kind: FromSecsErrorKind::Underflow }) + Err(FromSecsError { kind: FromSecsErrorKind::Negative }) } else { let nanos = nanos as u128; Ok(Duration { @@ -818,7 +818,7 @@ impl Duration { } else if nanos >= MAX_NANOS_F32 { Err(FromSecsError { kind: FromSecsErrorKind::Overflow }) } else if nanos < 0.0 { - Err(FromSecsError { kind: FromSecsErrorKind::Underflow }) + Err(FromSecsError { kind: FromSecsErrorKind::Negative }) } else { let nanos = nanos as u128; Ok(Duration { @@ -1274,11 +1274,9 @@ pub struct FromSecsError { impl FromSecsError { const fn description(&self) -> &'static str { match self.kind { - FromSecsErrorKind::NonFinite => { - "got non-finite value when converting float to duration" - } + FromSecsErrorKind::NonFinite => "non-finite value when converting float to duration", FromSecsErrorKind::Overflow => "overflow when converting float to duration", - FromSecsErrorKind::Underflow => "underflow when converting float to duration", + FromSecsErrorKind::Negative => "negative value when converting float to duration", } } } @@ -1292,10 +1290,10 @@ impl fmt::Display for FromSecsError { #[derive(Debug, Clone, PartialEq, Eq)] enum FromSecsErrorKind { - // Value is not a finite value (either infinity or NaN). + // Value is not a finite value (either + or - infinity or NaN). NonFinite, // Value is too large to store in a `Duration`. Overflow, - // Value is less than `0.0`. - Underflow, + // Value is negative. + Negative, } diff --git a/library/core/tests/atomic.rs b/library/core/tests/atomic.rs index b735957666f..7f8672f0354 100644 --- a/library/core/tests/atomic.rs +++ b/library/core/tests/atomic.rs @@ -220,3 +220,10 @@ fn atomic_compare_exchange() { ATOMIC.compare_exchange_weak(0, 1, SeqCst, Acquire).ok(); ATOMIC.compare_exchange_weak(0, 1, SeqCst, SeqCst).ok(); } + +#[test] +fn atomic_const_from() { + const _ATOMIC_U8: AtomicU8 = AtomicU8::from(1); + const _ATOMIC_BOOL: AtomicBool = AtomicBool::from(true); + const _ATOMIC_PTR: AtomicPtr<u32> = AtomicPtr::from(core::ptr::null_mut()); +} diff --git a/library/core/tests/cell.rs b/library/core/tests/cell.rs index 85a006c5d5b..4707cc7076e 100644 --- a/library/core/tests/cell.rs +++ b/library/core/tests/cell.rs @@ -465,4 +465,13 @@ fn const_cells() { const CELL: Cell<i32> = Cell::new(3); const _: i32 = CELL.into_inner(); + + const UNSAFE_CELL_FROM: UnsafeCell<i32> = UnsafeCell::from(3); + const _: i32 = UNSAFE_CELL.into_inner(); + + const REF_CELL_FROM: RefCell<i32> = RefCell::from(3); + const _: i32 = REF_CELL.into_inner(); + + const CELL_FROM: Cell<i32> = Cell::from(3); + const _: i32 = CELL.into_inner(); } diff --git a/library/core/tests/char.rs b/library/core/tests/char.rs index 51eca1e05d3..6e434cf1a8d 100644 --- a/library/core/tests/char.rs +++ b/library/core/tests/char.rs @@ -5,6 +5,8 @@ use std::{char, str}; #[test] fn test_convert() { assert_eq!(u32::from('a'), 0x61); + assert_eq!(u64::from('b'), 0x62); + assert_eq!(u128::from('c'), 0x63); assert_eq!(char::from(b'\0'), '\0'); assert_eq!(char::from(b'a'), 'a'); assert_eq!(char::from(b'\xFF'), '\u{FF}'); @@ -20,6 +22,16 @@ fn test_convert() { } #[test] +const fn test_convert_const() { + assert!(u32::from('a') == 0x61); + assert!(u64::from('b') == 0x62); + assert!(u128::from('c') == 0x63); + assert!(char::from(b'\0') == '\0'); + assert!(char::from(b'a') == 'a'); + assert!(char::from(b'\xFF') == '\u{FF}'); +} + +#[test] fn test_from_str() { assert_eq!(char::from_str("a").unwrap(), 'a'); assert_eq!(char::from_str("\0").unwrap(), '\0'); diff --git a/library/core/tests/fmt/float.rs b/library/core/tests/fmt/float.rs index bd0daf7a8eb..47a7400f76e 100644 --- a/library/core/tests/fmt/float.rs +++ b/library/core/tests/fmt/float.rs @@ -12,6 +12,16 @@ fn test_format_f64() { assert_eq!("1.23456789E3", format!("{:E}", 1234.56789f64)); assert_eq!("0.0", format!("{:?}", 0.0f64)); assert_eq!("1.01", format!("{:?}", 1.01f64)); + + let high_cutoff = 1e16_f64; + assert_eq!("1e16", format!("{:?}", high_cutoff)); + assert_eq!("-1e16", format!("{:?}", -high_cutoff)); + assert!(!is_exponential(&format!("{:?}", high_cutoff * (1.0 - 2.0 * f64::EPSILON)))); + assert_eq!("-3.0", format!("{:?}", -3f64)); + assert_eq!("0.0001", format!("{:?}", 0.0001f64)); + assert_eq!("9e-5", format!("{:?}", 0.00009f64)); + assert_eq!("1234567.9", format!("{:.1?}", 1234567.89f64)); + assert_eq!("1234.6", format!("{:.1?}", 1234.56789f64)); } #[test] @@ -28,4 +38,18 @@ fn test_format_f32() { assert_eq!("1.2345679E3", format!("{:E}", 1234.56789f32)); assert_eq!("0.0", format!("{:?}", 0.0f32)); assert_eq!("1.01", format!("{:?}", 1.01f32)); + + let high_cutoff = 1e16_f32; + assert_eq!("1e16", format!("{:?}", high_cutoff)); + assert_eq!("-1e16", format!("{:?}", -high_cutoff)); + assert!(!is_exponential(&format!("{:?}", high_cutoff * (1.0 - 2.0 * f32::EPSILON)))); + assert_eq!("-3.0", format!("{:?}", -3f32)); + assert_eq!("0.0001", format!("{:?}", 0.0001f32)); + assert_eq!("9e-5", format!("{:?}", 0.00009f32)); + assert_eq!("1234567.9", format!("{:.1?}", 1234567.89f32)); + assert_eq!("1234.6", format!("{:.1?}", 1234.56789f32)); +} + +fn is_exponential(s: &str) -> bool { + s.contains("e") || s.contains("E") } diff --git a/library/core/tests/lazy.rs b/library/core/tests/lazy.rs index 24f921ca7e4..064024ab87b 100644 --- a/library/core/tests/lazy.rs +++ b/library/core/tests/lazy.rs @@ -48,6 +48,12 @@ fn unsync_once_cell_drop_empty() { } #[test] +const fn once_cell_const() { + let _once_cell: OnceCell<u32> = OnceCell::new(); + let _once_cell: OnceCell<u32> = OnceCell::from(32); +} + +#[test] fn clone() { let s = OnceCell::new(); let c = s.clone(); diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 6958f07227a..ab0295c6314 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -9,6 +9,7 @@ #![feature(cfg_target_has_atomic)] #![feature(const_assume)] #![feature(const_cell_into_inner)] +#![feature(const_convert)] #![feature(const_maybe_uninit_assume_init)] #![cfg_attr(bootstrap, feature(const_panic))] #![feature(const_ptr_read)] diff --git a/library/core/tests/nonzero.rs b/library/core/tests/nonzero.rs index c2c08522d0c..a0ca919a851 100644 --- a/library/core/tests/nonzero.rs +++ b/library/core/tests/nonzero.rs @@ -204,9 +204,9 @@ fn nonzero_const() { // test that the methods of `NonZeroX>` are usable in a const context // Note: only tests NonZero8 - const NONZERO: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(5) }; + const NONZERO_U8: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(5) }; - const GET: u8 = NONZERO.get(); + const GET: u8 = NONZERO_U8.get(); assert_eq!(GET, 5); const ZERO: Option<NonZeroU8> = NonZeroU8::new(0); @@ -214,6 +214,12 @@ fn nonzero_const() { const ONE: Option<NonZeroU8> = NonZeroU8::new(1); assert!(ONE.is_some()); + + const FROM_NONZERO_U8: u8 = u8::from(NONZERO_U8); + assert_eq!(FROM_NONZERO_U8, 5); + + const NONZERO_CONVERT: NonZeroU32 = NonZeroU32::from(NONZERO_U8); + assert_eq!(NONZERO_CONVERT.get(), 5); } #[test] diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs index c9508c14525..cd07d6c52c2 100644 --- a/library/core/tests/option.rs +++ b/library/core/tests/option.rs @@ -358,10 +358,17 @@ fn option_const() { // test that the methods of `Option` are usable in a const context const OPTION: Option<usize> = Some(32); + assert_eq!(OPTION, Some(32)); + + const OPTION_FROM: Option<usize> = Option::from(32); + assert_eq!(OPTION_FROM, Some(32)); const REF: Option<&usize> = OPTION.as_ref(); assert_eq!(REF, Some(&32)); + const REF_FROM: Option<&usize> = Option::from(&OPTION); + assert_eq!(REF_FROM, Some(&32)); + const IS_SOME: bool = OPTION.is_some(); assert!(IS_SOME); @@ -388,6 +395,14 @@ const fn option_const_mut() { None => unreachable!(), } } + + { + let as_mut: Option<&mut usize> = Option::from(&mut option); + match as_mut { + Some(v) => *v = 42, + None => unreachable!(), + } + } } #[test] diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 5804701892e..546c43faecf 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -38,8 +38,8 @@ use super::map::{map_try_reserve_error, RandomState}; /// determined by the [`Eq`] trait, changes while it is in the set. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or /// unsafe code. The behavior resulting from such a logic error is not -/// specified, but will not result in undefined behavior. This could include -/// panics, incorrect results, aborts, memory leaks, and non-termination. +/// specified (it could include panics, incorrect results, aborts, memory +/// leaks, or non-termination) but will not be undefined behavior. /// /// # Examples /// diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs index 6827d3a8d24..cb09717bde5 100644 --- a/library/std/src/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -251,13 +251,12 @@ pub struct FromBytesWithNulError { /// # Examples /// /// ``` -/// #![feature(cstring_from_vec_with_nul)] /// use std::ffi::{CString, FromVecWithNulError}; /// /// let _: FromVecWithNulError = CString::from_vec_with_nul(b"f\0oo".to_vec()).unwrap_err(); /// ``` #[derive(Clone, PartialEq, Eq, Debug)] -#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] +#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] pub struct FromVecWithNulError { error_kind: FromBytesWithNulErrorKind, bytes: Vec<u8>, @@ -278,7 +277,7 @@ impl FromBytesWithNulError { } } -#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] +#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] impl FromVecWithNulError { /// Returns a slice of [`u8`]s bytes that were attempted to convert to a [`CString`]. /// @@ -287,7 +286,6 @@ impl FromVecWithNulError { /// Basic usage: /// /// ``` - /// #![feature(cstring_from_vec_with_nul)] /// use std::ffi::CString; /// /// // Some invalid bytes in a vector @@ -298,6 +296,7 @@ impl FromVecWithNulError { /// assert_eq!(&bytes[..], value.unwrap_err().as_bytes()); /// ``` #[must_use] + #[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] pub fn as_bytes(&self) -> &[u8] { &self.bytes[..] } @@ -313,7 +312,6 @@ impl FromVecWithNulError { /// Basic usage: /// /// ``` - /// #![feature(cstring_from_vec_with_nul)] /// use std::ffi::CString; /// /// // Some invalid bytes in a vector @@ -324,6 +322,7 @@ impl FromVecWithNulError { /// assert_eq!(bytes, value.unwrap_err().into_bytes()); /// ``` #[must_use = "`self` will be dropped if the result is not used"] + #[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] pub fn into_bytes(self) -> Vec<u8> { self.bytes } @@ -704,7 +703,6 @@ impl CString { /// # Example /// /// ``` - /// #![feature(cstring_from_vec_with_nul)] /// use std::ffi::CString; /// assert_eq!( /// unsafe { CString::from_vec_with_nul_unchecked(b"abc\0".to_vec()) }, @@ -712,7 +710,7 @@ impl CString { /// ); /// ``` #[must_use] - #[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] + #[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] pub unsafe fn from_vec_with_nul_unchecked(v: Vec<u8>) -> Self { Self { inner: v.into_boxed_slice() } } @@ -733,7 +731,6 @@ impl CString { /// when called without the ending nul byte. /// /// ``` - /// #![feature(cstring_from_vec_with_nul)] /// use std::ffi::CString; /// assert_eq!( /// CString::from_vec_with_nul(b"abc\0".to_vec()) @@ -745,14 +742,13 @@ impl CString { /// An incorrectly formatted [`Vec`] will produce an error. /// /// ``` - /// #![feature(cstring_from_vec_with_nul)] /// use std::ffi::{CString, FromVecWithNulError}; /// // Interior nul byte /// let _: FromVecWithNulError = CString::from_vec_with_nul(b"a\0bc".to_vec()).unwrap_err(); /// // No nul byte /// let _: FromVecWithNulError = CString::from_vec_with_nul(b"abc".to_vec()).unwrap_err(); /// ``` - #[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] + #[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] pub fn from_vec_with_nul(v: Vec<u8>) -> Result<Self, FromVecWithNulError> { let nul_pos = memchr::memchr(0, &v); match nul_pos { @@ -1084,10 +1080,10 @@ impl fmt::Display for FromBytesWithNulError { } } -#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] +#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] impl Error for FromVecWithNulError {} -#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] +#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] impl fmt::Display for FromVecWithNulError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.error_kind { diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index 82a76aa73c5..7f3bb836754 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -145,7 +145,7 @@ #[stable(feature = "cstr_from_bytes", since = "1.10.0")] pub use self::c_str::FromBytesWithNulError; -#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] +#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] pub use self::c_str::FromVecWithNulError; #[stable(feature = "rust1", since = "1.0.0")] pub use self::c_str::{CStr, CString, IntoStringError, NulError}; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 1d2d26b8f00..e22748a4c8d 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -324,7 +324,6 @@ #![feature(ptr_internals)] #![feature(rustc_attrs)] #![feature(rustc_private)] -#![feature(saturating_div)] #![feature(saturating_int_impl)] #![feature(slice_concat_ext)] #![feature(slice_internals)] diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index e5e9fedb61e..c080f783cbb 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -59,7 +59,8 @@ pub enum IpAddr { /// /// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal /// notation, divided by `.` (this is called "dot-decimal notation"). -/// Notably, octal numbers and hexadecimal numbers are not allowed per [IETF RFC 6943]. +/// Notably, octal numbers (which are indicated with a leading `0`) and hexadecimal numbers (which +/// are indicated with a leading `0x`) are not allowed per [IETF RFC 6943]. /// /// [IETF RFC 6943]: https://tools.ietf.org/html/rfc6943#section-3.1.1 /// [`FromStr`]: crate::str::FromStr @@ -72,6 +73,9 @@ pub enum IpAddr { /// let localhost = Ipv4Addr::new(127, 0, 0, 1); /// assert_eq!("127.0.0.1".parse(), Ok(localhost)); /// assert_eq!(localhost.is_loopback(), true); +/// assert!("012.004.002.000".parse::<Ipv4Addr>().is_err()); // all octets are in octal +/// assert!("0000000.0.0.0".parse::<Ipv4Addr>().is_err()); // first octet is a zero in octal +/// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex /// ``` #[derive(Copy)] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs index babc854cd1d..17581f33026 100644 --- a/library/std/src/net/ip/tests.rs +++ b/library/std/src/net/ip/tests.rs @@ -20,6 +20,14 @@ fn test_from_str_ipv4() { // no number between dots let none: Option<Ipv4Addr> = "255.0..1".parse().ok(); assert_eq!(None, none); + // octal + let none: Option<Ipv4Addr> = "255.0.0.01".parse().ok(); + assert_eq!(None, none); + // octal zero + let none: Option<Ipv4Addr> = "255.0.0.00".parse().ok(); + assert_eq!(None, none); + let none: Option<Ipv4Addr> = "255.0.00.0".parse().ok(); + assert_eq!(None, none); } #[test] diff --git a/library/std/src/net/parser.rs b/library/std/src/net/parser.rs index 88a8cb76bef..4e16a55edec 100644 --- a/library/std/src/net/parser.rs +++ b/library/std/src/net/parser.rs @@ -111,10 +111,12 @@ impl<'a> Parser<'a> { &mut self, radix: u32, max_digits: Option<usize>, + allow_zero_prefix: bool, ) -> Option<T> { self.read_atomically(move |p| { let mut result = T::ZERO; let mut digit_count = 0; + let has_leading_zero = p.peek_char() == Some('0'); while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { result = result.checked_mul(radix)?; @@ -127,7 +129,13 @@ impl<'a> Parser<'a> { } } - if digit_count == 0 { None } else { Some(result) } + if digit_count == 0 { + None + } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { + None + } else { + Some(result) + } }) } @@ -140,10 +148,7 @@ impl<'a> Parser<'a> { *slot = p.read_separator('.', i, |p| { // Disallow octal number in IP string. // https://tools.ietf.org/html/rfc6943#section-3.1.1 - match (p.peek_char(), p.read_number(10, None)) { - (Some('0'), Some(number)) if number != 0 => None, - (_, number) => number, - } + p.read_number(10, Some(3), false) })?; } @@ -175,7 +180,7 @@ impl<'a> Parser<'a> { } } - let group = p.read_separator(':', i, |p| p.read_number(16, Some(4))); + let group = p.read_separator(':', i, |p| p.read_number(16, Some(4), true)); match group { Some(g) => *slot = g, @@ -227,7 +232,7 @@ impl<'a> Parser<'a> { fn read_port(&mut self) -> Option<u16> { self.read_atomically(|p| { p.read_given_char(':')?; - p.read_number(10, None) + p.read_number(10, None, true) }) } @@ -235,7 +240,7 @@ impl<'a> Parser<'a> { fn read_scope_id(&mut self) -> Option<u32> { self.read_atomically(|p| { p.read_given_char('%')?; - p.read_number(10, None) + p.read_number(10, None, true) }) } @@ -281,7 +286,12 @@ impl FromStr for IpAddr { impl FromStr for Ipv4Addr { type Err = AddrParseError; fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> { - Parser::new(s).parse_with(|p| p.read_ipv4_addr()) + // don't try to parse if too long + if s.len() > 15 { + Err(AddrParseError(())) + } else { + Parser::new(s).parse_with(|p| p.read_ipv4_addr()) + } } } diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 21e9669c110..c0605b2f412 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -25,7 +25,7 @@ pub macro panic_2015 { $crate::rt::panic_display(&$arg) }), ($fmt:expr, $($arg:tt)+) => ({ - $crate::rt::begin_panic_fmt(&$crate::const_format_args!($fmt, $($arg)+)) + $crate::rt::panic_fmt($crate::const_format_args!($fmt, $($arg)+)) }), } diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 231c9fc19c0..56646b72dd5 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -437,31 +437,9 @@ pub fn panicking() -> bool { !panic_count::count_is_zero() } -/// The entry point for panicking with a formatted message. -/// -/// This is designed to reduce the amount of code required at the call -/// site as much as possible (so that `panic!()` has as low an impact -/// on (e.g.) the inlining of other functions as possible), by moving -/// the actual formatting into this shared place. -#[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")] -#[cold] -// If panic_immediate_abort, inline the abort call, -// otherwise avoid inlining because of it is cold path. -#[cfg_attr(not(feature = "panic_immediate_abort"), track_caller)] -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] -#[cfg_attr(feature = "panic_immediate_abort", inline)] -#[cfg_attr(not(test), lang = "begin_panic_fmt")] -pub fn begin_panic_fmt(msg: &fmt::Arguments<'_>) -> ! { - if cfg!(feature = "panic_immediate_abort") { - intrinsics::abort() - } - - let info = PanicInfo::internal_constructor(Some(msg), Location::caller()); - begin_panic_handler(&info) -} - /// Entry point of panics from the libcore crate (`panic_impl` lang item). -#[cfg_attr(not(test), panic_handler)] +#[cfg(not(test))] +#[panic_handler] pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { struct PanicPayload<'a> { inner: &'a fmt::Arguments<'a>, diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 47156dc33e5..8f00d2260e4 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1208,6 +1208,9 @@ impl PathBuf { /// * if `path` has a root but no prefix (e.g., `\windows`), it /// replaces everything except for the prefix (if any) of `self`. /// * if `path` has a prefix but no root, it replaces `self`. + /// * if `self` has a verbatim prefix (e.g. `\\?\C:\windows`) + /// and `path` is not empty, the new path is normalized: all references + /// to `.` and `..` are removed. /// /// # Examples /// @@ -1254,7 +1257,7 @@ impl PathBuf { self.as_mut_vec().truncate(0); // verbatim paths need . and .. removed - } else if comps.prefix_verbatim() { + } else if comps.prefix_verbatim() && !path.inner.is_empty() { let mut buf: Vec<_> = comps.collect(); for c in path.components() { match c { diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index 3973a6829d3..0a16ff2a721 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1271,6 +1271,7 @@ pub fn test_push() { tp!(r"\\?\A:\x\y", "/foo", r"\\?\A:\foo"); tp!(r"\\?\A:", r"..\foo\.", r"\\?\A:\foo"); tp!(r"\\?\A:\x\y", r".\foo\.", r"\\?\A:\x\y\foo"); + tp!(r"\\?\A:\x\y", r"", r"\\?\A:\x\y\"); } } diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 4d72aff0116..121c214780d 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -19,8 +19,8 @@ use crate::ffi::CString; // Re-export some of our utilities which are expected by other crates. -pub use crate::panicking::{begin_panic, begin_panic_fmt, panic_count}; -pub use core::panicking::panic_display; +pub use crate::panicking::{begin_panic, panic_count}; +pub use core::panicking::{panic_display, panic_fmt}; use crate::sync::Once; use crate::sys; diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 133c3e46cd8..11836b7b694 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -130,7 +130,7 @@ impl Barrier { if lock.count < self.num_threads { // We need a while loop to guard against spurious wakeups. // https://en.wikipedia.org/wiki/Spurious_wakeup - while local_gen == lock.generation_id && lock.count < self.num_threads { + while local_gen == lock.generation_id { lock = self.cvar.wait(lock).unwrap(); } BarrierWaitResult(false) diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 9a48b768cb3..8d03aade341 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -90,6 +90,7 @@ pub struct Config { // llvm codegen options pub llvm_skip_rebuild: bool, pub llvm_assertions: bool, + pub llvm_tests: bool, pub llvm_plugins: bool, pub llvm_optimize: bool, pub llvm_thin_lto: bool, @@ -422,6 +423,7 @@ struct Llvm { thin_lto: Option<bool>, release_debuginfo: Option<bool>, assertions: Option<bool>, + tests: Option<bool>, plugins: Option<bool>, ccache: Option<StringOrBool>, version_check: Option<bool>, @@ -715,6 +717,7 @@ impl Config { // Store off these values as options because if they're not provided // we'll infer default values for them later let mut llvm_assertions = None; + let mut llvm_tests = None; let mut llvm_plugins = None; let mut debug = None; let mut debug_assertions = None; @@ -740,6 +743,7 @@ impl Config { } set(&mut config.ninja_in_file, llvm.ninja); llvm_assertions = llvm.assertions; + llvm_tests = llvm.tests; llvm_plugins = llvm.plugins; llvm_skip_rebuild = llvm_skip_rebuild.or(llvm.skip_rebuild); set(&mut config.llvm_optimize, llvm.optimize); @@ -991,6 +995,7 @@ impl Config { config.llvm_skip_rebuild = llvm_skip_rebuild.unwrap_or(false); config.llvm_assertions = llvm_assertions.unwrap_or(false); + config.llvm_tests = llvm_tests.unwrap_or(false); config.llvm_plugins = llvm_plugins.unwrap_or(false); config.rust_optimize = optimize.unwrap_or(true); diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 27c9bb2504f..6bfaeffa807 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -170,6 +170,7 @@ impl Step for Llvm { let assertions = if builder.config.llvm_assertions { "ON" } else { "OFF" }; let plugins = if builder.config.llvm_plugins { "ON" } else { "OFF" }; + let enable_tests = if builder.config.llvm_tests { "ON" } else { "OFF" }; cfg.out_dir(&out_dir) .profile(profile) @@ -180,7 +181,7 @@ impl Step for Llvm { .define("LLVM_INCLUDE_EXAMPLES", "OFF") .define("LLVM_INCLUDE_DOCS", "OFF") .define("LLVM_INCLUDE_BENCHMARKS", "OFF") - .define("LLVM_INCLUDE_TESTS", "OFF") + .define("LLVM_INCLUDE_TESTS", enable_tests) .define("LLVM_ENABLE_TERMINFO", "OFF") .define("LLVM_ENABLE_LIBEDIT", "OFF") .define("LLVM_ENABLE_BINDINGS", "OFF") diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index adc970c66d6..ba4e1ca3114 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -72,7 +72,7 @@ ENV PATH="/node-v14.4.0-linux-x64/bin:${PATH}" # https://github.com/puppeteer/puppeteer/issues/375 # # We also specify the version in case we need to update it to go around cache limitations. -RUN npm install -g browser-ui-test@0.4.3 --unsafe-perm=true +RUN npm install -g browser-ui-test@0.4.5 --unsafe-perm=true ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 9d102d68783..075efd29b59 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -421,7 +421,7 @@ impl Clean<GenericParamDef> for ty::GenericParamDef { GenericParamDefKind::Type { did: self.def_id, bounds: vec![], // These are filled in from the where-clauses. - default, + default: default.map(Box::new), synthetic, }, ) @@ -430,9 +430,9 @@ impl Clean<GenericParamDef> for ty::GenericParamDef { self.name, GenericParamDefKind::Const { did: self.def_id, - ty: cx.tcx.type_of(self.def_id).clean(cx), + ty: Box::new(cx.tcx.type_of(self.def_id).clean(cx)), default: match has_default { - true => Some(cx.tcx.const_param_default(self.def_id).to_string()), + true => Some(Box::new(cx.tcx.const_param_default(self.def_id).to_string())), false => None, }, }, @@ -462,7 +462,7 @@ impl Clean<GenericParamDef> for hir::GenericParam<'_> { GenericParamDefKind::Type { did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), bounds: self.bounds.clean(cx), - default: default.clean(cx), + default: default.clean(cx).map(Box::new), synthetic, }, ), @@ -470,10 +470,10 @@ impl Clean<GenericParamDef> for hir::GenericParam<'_> { self.name.ident().name, GenericParamDefKind::Const { did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), - ty: ty.clean(cx), + ty: Box::new(ty.clean(cx)), default: default.map(|ct| { let def_id = cx.tcx.hir().local_def_id(ct.hir_id); - ty::Const::from_anon_const(cx.tcx, def_id).to_string() + Box::new(ty::Const::from_anon_const(cx.tcx, def_id).to_string()) }), }, ), diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 9dab0023ba3..d25e166629f 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1219,13 +1219,13 @@ crate enum GenericParamDefKind { Type { did: DefId, bounds: Vec<GenericBound>, - default: Option<Type>, + default: Option<Box<Type>>, synthetic: Option<hir::SyntheticTyParamKind>, }, Const { did: DefId, - ty: Type, - default: Option<String>, + ty: Box<Type>, + default: Option<Box<String>>, }, } @@ -1239,8 +1239,8 @@ impl GenericParamDefKind { // any embedded types, but `get_type` seems to be the wrong name for that. crate fn get_type(&self) -> Option<Type> { match self { - GenericParamDefKind::Type { default, .. } => default.clone(), - GenericParamDefKind::Const { ty, .. } => Some(ty.clone()), + GenericParamDefKind::Type { default, .. } => default.as_deref().cloned(), + GenericParamDefKind::Const { ty, .. } => Some((&**ty).clone()), GenericParamDefKind::Lifetime { .. } => None, } } @@ -1252,6 +1252,10 @@ crate struct GenericParamDef { crate kind: GenericParamDefKind, } +// `GenericParamDef` is used in many places. Make sure it doesn't unintentionally get bigger. +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(GenericParamDef, 56); + impl GenericParamDef { crate fn is_synthetic_type_param(&self) -> bool { match self.kind { diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 4cfc57ac995..58cd1018c31 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -482,24 +482,26 @@ fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean:: + name.as_str().len() + generics_len; - wrap_item(w, "fn", |w| { - render_attributes_in_pre(w, it, ""); - w.reserve(header_len); - write!( - w, - "{vis}{constness}{asyncness}{unsafety}{abi}fn \ - {name}{generics}{decl}{notable_traits}{where_clause}", - vis = vis, - constness = constness, - asyncness = asyncness, - unsafety = unsafety, - abi = abi, - name = name, - generics = f.generics.print(cx), - where_clause = print_where_clause(&f.generics, cx, 0, true), - decl = f.decl.full_print(header_len, 0, f.header.asyncness, cx), - notable_traits = notable_traits_decl(&f.decl, cx), - ); + wrap_into_docblock(w, |w| { + wrap_item(w, "fn", |w| { + render_attributes_in_pre(w, it, ""); + w.reserve(header_len); + write!( + w, + "{vis}{constness}{asyncness}{unsafety}{abi}fn \ + {name}{generics}{decl}{notable_traits}{where_clause}", + vis = vis, + constness = constness, + asyncness = asyncness, + unsafety = unsafety, + abi = abi, + name = name, + generics = f.generics.print(cx), + where_clause = print_where_clause(&f.generics, cx, 0, true), + decl = f.decl.full_print(header_len, 0, f.header.asyncness, cx), + notable_traits = notable_traits_decl(&f.decl, cx), + ); + }); }); document(w, cx, it, None, HeadingOffset::H2) } @@ -844,16 +846,18 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra } fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { - wrap_item(w, "trait-alias", |w| { - render_attributes_in_pre(w, it, ""); - write!( - w, - "trait {}{}{} = {};", - it.name.as_ref().unwrap(), - t.generics.print(cx), - print_where_clause(&t.generics, cx, 0, true), - bounds(&t.bounds, true, cx) - ); + wrap_into_docblock(w, |w| { + wrap_item(w, "trait-alias", |w| { + render_attributes_in_pre(w, it, ""); + write!( + w, + "trait {}{}{} = {};", + it.name.as_ref().unwrap(), + t.generics.print(cx), + print_where_clause(&t.generics, cx, 0, true), + bounds(&t.bounds, true, cx) + ); + }); }); document(w, cx, it, None, HeadingOffset::H2); @@ -866,16 +870,18 @@ fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clea } fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { - wrap_item(w, "opaque", |w| { - render_attributes_in_pre(w, it, ""); - write!( - w, - "type {}{}{where_clause} = impl {bounds};", - it.name.as_ref().unwrap(), - t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, true), - bounds = bounds(&t.bounds, false, cx), - ); + wrap_into_docblock(w, |w| { + wrap_item(w, "opaque", |w| { + render_attributes_in_pre(w, it, ""); + write!( + w, + "type {}{}{where_clause} = impl {bounds};", + it.name.as_ref().unwrap(), + t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, true), + bounds = bounds(&t.bounds, false, cx), + ); + }); }); document(w, cx, it, None, HeadingOffset::H2); @@ -894,20 +900,37 @@ fn item_typedef( t: &clean::Typedef, is_associated: bool, ) { - wrap_item(w, "typedef", |w| { - render_attributes_in_pre(w, it, ""); - if !is_associated { - write!(w, "{}", it.visibility.print_with_space(it.def_id, cx)); - } - write!( - w, - "type {}{}{where_clause} = {type_};", - it.name.as_ref().unwrap(), - t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, true), - type_ = t.type_.print(cx), - ); - }); + fn write_content( + w: &mut Buffer, + cx: &Context<'_>, + it: &clean::Item, + t: &clean::Typedef, + is_associated: bool, + ) { + wrap_item(w, "typedef", |w| { + render_attributes_in_pre(w, it, ""); + if !is_associated { + write!(w, "{}", it.visibility.print_with_space(it.def_id, cx)); + } + write!( + w, + "type {}{}{where_clause} = {type_};", + it.name.as_ref().unwrap(), + t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, true), + type_ = t.type_.print(cx), + ); + }); + } + + // If this is an associated typedef, we don't want to wrap it into a docblock. + if is_associated { + write_content(w, cx, it, t, is_associated); + } else { + wrap_into_docblock(w, |w| { + write_content(w, cx, it, t, is_associated); + }); + } document(w, cx, it, None, HeadingOffset::H2); @@ -1142,32 +1165,34 @@ fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Mac } fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { - let name = it.name.as_ref().expect("proc-macros always have names"); - match m.kind { - MacroKind::Bang => { - wrap_item(w, "macro", |w| { - write!(w, "{}!() {{ /* proc-macro */ }}", name); - }); - } - MacroKind::Attr => { - wrap_item(w, "attr", |w| { - write!(w, "#[{}]", name); - }); - } - MacroKind::Derive => { - wrap_item(w, "derive", |w| { - write!(w, "#[derive({})]", name); - if !m.helpers.is_empty() { - w.push_str("\n{\n"); - w.push_str(" // Attributes available to this derive:\n"); - for attr in &m.helpers { - writeln!(w, " #[{}]", attr); + wrap_into_docblock(w, |w| { + let name = it.name.as_ref().expect("proc-macros always have names"); + match m.kind { + MacroKind::Bang => { + wrap_item(w, "macro", |w| { + write!(w, "{}!() {{ /* proc-macro */ }}", name); + }); + } + MacroKind::Attr => { + wrap_item(w, "attr", |w| { + write!(w, "#[{}]", name); + }); + } + MacroKind::Derive => { + wrap_item(w, "derive", |w| { + write!(w, "#[derive({})]", name); + if !m.helpers.is_empty() { + w.push_str("\n{\n"); + w.push_str(" // Attributes available to this derive:\n"); + for attr in &m.helpers { + writeln!(w, " #[{}]", attr); + } + w.push_str("}\n"); } - w.push_str("}\n"); - } - }); + }); + } } - } + }); document(w, cx, it, None, HeadingOffset::H2) } @@ -1177,38 +1202,40 @@ fn item_primitive(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { } fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) { - wrap_item(w, "const", |w| { - render_attributes_in_code(w, it); + wrap_into_docblock(w, |w| { + wrap_item(w, "const", |w| { + render_attributes_in_code(w, it); - write!( - w, - "{vis}const {name}: {typ}", - vis = it.visibility.print_with_space(it.def_id, cx), - name = it.name.as_ref().unwrap(), - typ = c.type_.print(cx), - ); + write!( + w, + "{vis}const {name}: {typ}", + vis = it.visibility.print_with_space(it.def_id, cx), + name = it.name.as_ref().unwrap(), + typ = c.type_.print(cx), + ); - let value = c.value(cx.tcx()); - let is_literal = c.is_literal(cx.tcx()); - let expr = c.expr(cx.tcx()); - if value.is_some() || is_literal { - write!(w, " = {expr};", expr = Escape(&expr)); - } else { - w.write_str(";"); - } + let value = c.value(cx.tcx()); + let is_literal = c.is_literal(cx.tcx()); + let expr = c.expr(cx.tcx()); + if value.is_some() || is_literal { + write!(w, " = {expr};", expr = Escape(&expr)); + } else { + w.write_str(";"); + } - if !is_literal { - if let Some(value) = &value { - let value_lowercase = value.to_lowercase(); - let expr_lowercase = expr.to_lowercase(); + if !is_literal { + if let Some(value) = &value { + let value_lowercase = value.to_lowercase(); + let expr_lowercase = expr.to_lowercase(); - if value_lowercase != expr_lowercase - && value_lowercase.trim_end_matches("i32") != expr_lowercase - { - write!(w, " // {value}", value = Escape(value)); + if value_lowercase != expr_lowercase + && value_lowercase.trim_end_matches("i32") != expr_lowercase + { + write!(w, " // {value}", value = Escape(value)); + } } } - } + }); }); document(w, cx, it, None, HeadingOffset::H2) @@ -1268,30 +1295,34 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St } fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) { - wrap_item(w, "static", |w| { - render_attributes_in_code(w, it); - write!( - w, - "{vis}static {mutability}{name}: {typ}", - vis = it.visibility.print_with_space(it.def_id, cx), - mutability = s.mutability.print_with_space(), - name = it.name.as_ref().unwrap(), - typ = s.type_.print(cx) - ); + wrap_into_docblock(w, |w| { + wrap_item(w, "static", |w| { + render_attributes_in_code(w, it); + write!( + w, + "{vis}static {mutability}{name}: {typ}", + vis = it.visibility.print_with_space(it.def_id, cx), + mutability = s.mutability.print_with_space(), + name = it.name.as_ref().unwrap(), + typ = s.type_.print(cx) + ); + }); }); document(w, cx, it, None, HeadingOffset::H2) } fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { - wrap_item(w, "foreigntype", |w| { - w.write_str("extern {\n"); - render_attributes_in_code(w, it); - write!( - w, - " {}type {};\n}}", - it.visibility.print_with_space(it.def_id, cx), - it.name.as_ref().unwrap(), - ); + wrap_into_docblock(w, |w| { + wrap_item(w, "foreigntype", |w| { + w.write_str("extern {\n"); + render_attributes_in_code(w, it); + write!( + w, + " {}type {};\n}}", + it.visibility.print_with_space(it.def_id, cx), + it.name.as_ref().unwrap(), + ); + }); }); document(w, cx, it, None, HeadingOffset::H2); @@ -1374,7 +1405,7 @@ fn wrap_into_docblock<F>(w: &mut Buffer, f: F) where F: FnOnce(&mut Buffer), { - w.write_str("<div class=\"docblock type-decl\">"); + w.write_str("<div class=\"docblock item-decl\">"); f(w); w.write_str("</div>") } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 11c54876dea..e41c993a528 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -129,9 +129,14 @@ h3 { } h1, h2, h3, h4, h5, h6 { font-weight: 500; +} +h1, h2, h3, h4 { margin: 20px 0 15px 0; padding-bottom: 6px; } +h5, h6 { + margin: 15px 0 5px 0; +} h1.fqn { display: flex; border-bottom: 1px dashed; @@ -254,7 +259,10 @@ code, pre, a.test-arrow, .code-header { pre { padding: 14px; } -.type-decl pre { +.docblock.item-decl { + margin-left: 0; +} +.item-decl pre { overflow-x: auto; } @@ -502,14 +510,12 @@ nav.sub { white-space: pre-wrap; } -.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5, .docblock h6 { - border-bottom: 1px solid; -} - .top-doc .docblock h2 { font-size: 1.3em; } .top-doc .docblock h3 { font-size: 1.15em; } .top-doc .docblock h4, -.top-doc .docblock h5, +.top-doc .docblock h5 { + font-size: 1.1em; +} .top-doc .docblock h6 { font-size: 1em; } @@ -550,6 +556,7 @@ nav.sub { flex-grow: 1; margin: 0px; padding: 0px; + overflow-wrap: anywhere; } .in-band > code, .in-band > .code-header { diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 0fd6462a8f5..ccb1a707032 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -220,7 +220,7 @@ body.source .example-wrap pre.rust a { background: #333; } -.docblock:not(.type-decl) a:not(.srclink):not(.test-arrow), +.docblock:not(.item-decl) a:not(.srclink):not(.test-arrow), .docblock-short a:not(.srclink):not(.test-arrow), .item-info a, #help a { color: #39AFD7; diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index d863701dd73..93801af46ec 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -181,7 +181,7 @@ body.source .example-wrap pre.rust a { background: #333; } -.docblock:not(.type-decl) a:not(.srclink):not(.test-arrow), +.docblock:not(.item-decl) a:not(.srclink):not(.test-arrow), .docblock-short a:not(.srclink):not(.test-arrow), .item-info a, #help a { color: #D2991D; diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 28d2e99a3d0..fba8231caac 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -176,7 +176,7 @@ body.source .example-wrap pre.rust a { background: #eee; } -.docblock:not(.type-decl) a:not(.srclink):not(.test-arrow), +.docblock:not(.item-decl) a:not(.srclink):not(.test-arrow), .docblock-short a:not(.srclink):not(.test-arrow), .item-info a, #help a { color: #3873AD; diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 4098f17db81..924275dc185 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -330,10 +330,10 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind { }, Type { did: _, bounds, default, synthetic: _ } => GenericParamDefKind::Type { bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(), - default: default.map(|x| x.into_tcx(tcx)), + default: default.map(|x| (*x).into_tcx(tcx)), }, Const { did: _, ty, default } => { - GenericParamDefKind::Const { ty: ty.into_tcx(tcx), default } + GenericParamDefKind::Const { ty: (*ty).into_tcx(tcx), default: default.map(|x| *x) } } } } diff --git a/src/test/codegen/debug-vtable.rs b/src/test/codegen/debug-vtable.rs index 851d68da5ee..1c8cc61f204 100644 --- a/src/test/codegen/debug-vtable.rs +++ b/src/test/codegen/debug-vtable.rs @@ -18,6 +18,9 @@ // MSVC-LABEL: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, _>::vtable$" // CHECK: !DISubrange(count: 3 +// NONMSVC-LABEL: !DIGlobalVariable(name: "<debug_vtable::bar::{closure#0} as core::ops::function::FnOnce<(core::option::Option<&dyn core::ops::function::Fn<(), Output=()>>)>>::{vtable}" +// MSVC-LABEL: !DIGlobalVariable(name: "impl$<debug_vtable::bar::closure$0, core::ops::function::FnOnce<tuple$<enum$<core::option::Option<ref$<dyn$<core::ops::function::Fn<tuple$<>,assoc$<Output,tuple$<> > > > > >, {{.*}}, {{.*}}, Some> > > >::vtable$" + #![crate_type = "lib"] pub struct Foo; @@ -45,3 +48,10 @@ pub fn foo(x: &Foo) -> (u32, (u64, i8), &dyn Send) { let z: &dyn SomeTraitWithGenerics<u64, i8> = x; (y.method1(), z.method1(), x as &dyn Send) } + +// Constructing the debuginfo name for the FnOnce vtable below initially caused an ICE on MSVC +// because the trait type contains a late bound region that needed to be erased before the type +// layout for the niche enum `Option<&dyn Fn()>` could be computed. +pub fn bar() -> Box<dyn FnOnce(Option<&dyn Fn()>)> { + Box::new(|_x: Option<&dyn Fn()>| {}) +} diff --git a/src/test/incremental/auxiliary/rustc-rust-log-aux.rs b/src/test/incremental/auxiliary/rustc-rust-log-aux.rs new file mode 100644 index 00000000000..a361373dc19 --- /dev/null +++ b/src/test/incremental/auxiliary/rustc-rust-log-aux.rs @@ -0,0 +1,8 @@ +// rustc-env:RUSTC_LOG=debug +#[cfg(rpass1)] +pub fn foo() {} + +#[cfg(rpass2)] +pub fn foo() { + println!(); +} diff --git a/src/test/incremental/hashes/for_loops.rs b/src/test/incremental/hashes/for_loops.rs index 5a944d28a0b..1b96cd54c3e 100644 --- a/src/test/incremental/hashes/for_loops.rs +++ b/src/test/incremental/hashes/for_loops.rs @@ -83,7 +83,7 @@ pub fn change_iteration_variable_pattern() { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2", except="hir_owner_nodes, optimized_mir, typeck")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, optimized_mir, typeck, promoted_mir")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, optimized_mir, typeck")] #[rustc_clean(cfg="cfail6")] pub fn change_iteration_variable_pattern() { let mut _x = 0; @@ -108,7 +108,7 @@ pub fn change_iterable() { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2", except="hir_owner_nodes, promoted_mir")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, promoted_mir, optimized_mir")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, promoted_mir")] #[rustc_clean(cfg="cfail6")] pub fn change_iterable() { let mut _x = 0; @@ -183,7 +183,7 @@ pub fn add_loop_label_to_break() { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, optimized_mir")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail6")] pub fn add_loop_label_to_break() { let mut _x = 0; @@ -237,7 +237,7 @@ pub fn add_loop_label_to_continue() { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, optimized_mir")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail6")] pub fn add_loop_label_to_continue() { let mut _x = 0; diff --git a/src/test/incremental/rustc-rust-log.rs b/src/test/incremental/rustc-rust-log.rs new file mode 100644 index 00000000000..566f0d96d97 --- /dev/null +++ b/src/test/incremental/rustc-rust-log.rs @@ -0,0 +1,16 @@ +// revisions: rpass1 rpass2 +// This test is just checking that we won't ICE if logging is turned +// on; don't bother trying to compare that (copious) output. +// +// dont-check-compiler-stdout +// dont-check-compiler-stderr +// aux-build: rustc-rust-log-aux.rs +// rustc-env:RUSTC_LOG=debug + +#[cfg(rpass1)] +fn main() {} + +#[cfg(rpass2)] +fn main() { + println!(); +} diff --git a/src/test/rustdoc-gui/basic.goml b/src/test/rustdoc-gui/basic.goml index 44fcec33937..239e51a9129 100644 --- a/src/test/rustdoc-gui/basic.goml +++ b/src/test/rustdoc-gui/basic.goml @@ -1,4 +1,4 @@ goto: file://|DOC_PATH|/test_docs/index.html assert: ("#functions") goto: ./struct.Foo.html -assert: ("div.type-decl") +assert: ("div.item-decl") diff --git a/src/test/rustdoc-gui/check-code-blocks-margin.goml b/src/test/rustdoc-gui/check-code-blocks-margin.goml new file mode 100644 index 00000000000..2de47682856 --- /dev/null +++ b/src/test/rustdoc-gui/check-code-blocks-margin.goml @@ -0,0 +1,6 @@ +// 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"}) +// ... but all the others should! +assert-css: ("#main .docblock:not(.item-decl)", {"margin-left": "24px"}) diff --git a/src/test/rustdoc-gui/docblock-code-block-line-number.goml b/src/test/rustdoc-gui/docblock-code-block-line-number.goml new file mode 100644 index 00000000000..7e6607b55ea --- /dev/null +++ b/src/test/rustdoc-gui/docblock-code-block-line-number.goml @@ -0,0 +1,22 @@ +// Checks that the setting "line numbers" is working as expected. +goto: file://|DOC_PATH|/test_docs/fn.foo.html + +// We check that without this setting, there is no line number displayed. +assert-false: "pre.line-number" + +// We now set the setting to show the line numbers on code examples. +local-storage: {"rustdoc-line-numbers": "true" } +// We reload to make the line numbers appear. +reload: + +// We wait for them to be added into the DOM by the JS... +wait-for: "pre.line-number" +// If the test didn't fail, it means that it was found! +// Let's now check some CSS properties... +assert-css: ("pre.line-number", { + "margin": "0px", + "padding": "13px 8px", + "text-align": "right" +}) +// The first code block has two lines so let's check its `<pre>` elements lists both of them. +assert-text: ("pre.line-number", "1\n2") diff --git a/src/test/rustdoc-gui/font-weight.goml b/src/test/rustdoc-gui/font-weight.goml index d8411511c5a..0459fd4b9c3 100644 --- a/src/test/rustdoc-gui/font-weight.goml +++ b/src/test/rustdoc-gui/font-weight.goml @@ -1,6 +1,6 @@ goto: file://|DOC_PATH|/lib2/struct.Foo.html // This test checks that the font weight is correctly applied. -assert-css: ("//*[@class='docblock type-decl']//a[text()='Alias']", {"font-weight": "400"}) +assert-css: ("//*[@class='docblock item-decl']//a[text()='Alias']", {"font-weight": "400"}) assert-css: ("//*[@class='structfield small-section-header']//a[text()='Alias']", {"font-weight": "400"}) assert-css: ("#method\.a_method > .code-header", {"font-weight": "600"}) assert-css: ("#associatedtype\.X > .code-header", {"font-weight": "600"}) @@ -16,7 +16,7 @@ goto: file://|DOC_PATH|/lib2/trait.Trait.html // This is a complex selector, so here's how it works: // -// * //*[@class='docblock type-decl'] — selects element of any tag with classes docblock and type-decl +// * //*[@class='docblock item-decl'] — selects element of any tag with classes docblock and item-decl // * /pre[@class='rust trait'] — selects immediate child with tag pre and classes rust and trait // * /code — selects immediate child with tag code // * /a[@class='constant'] — selects immediate child with tag a and class constant @@ -25,8 +25,8 @@ goto: file://|DOC_PATH|/lib2/trait.Trait.html // // This uses '/parent::*' as a proxy for the style of the text node. // We can't just select the '<a>' because intermediate tags could be added. -assert-count: ("//*[@class='docblock type-decl']/pre[@class='rust trait']/code/a[@class='constant']//text()/parent::*", 1) -assert-css: ("//*[@class='docblock type-decl']/pre[@class='rust trait']/code/a[@class='constant']//text()/parent::*", {"font-weight": "400"}) +assert-count: ("//*[@class='docblock item-decl']/pre[@class='rust trait']/code/a[@class='constant']//text()/parent::*", 1) +assert-css: ("//*[@class='docblock item-decl']/pre[@class='rust trait']/code/a[@class='constant']//text()/parent::*", {"font-weight": "400"}) assert-count: (".methods .type", 1) assert-css: (".methods .type", {"font-weight": "600"}) diff --git a/src/test/rustdoc-gui/sidebar-macro-reexport.goml b/src/test/rustdoc-gui/sidebar-macro-reexport.goml new file mode 100644 index 00000000000..a3a62fe5446 --- /dev/null +++ b/src/test/rustdoc-gui/sidebar-macro-reexport.goml @@ -0,0 +1,5 @@ +// This test ensures that the reexport of a macro doesn't make the original macro +// displayed twice in the sidebar. +goto: file://|DOC_PATH|/test_docs/macro.repro.html +wait-for: ".sidebar-elems .macro .macro" +assert-count: ("//*[@class='sidebar-elems']//*[@class='block macro']//a[text()='repro']", 1) diff --git a/src/test/rustdoc-gui/sidebar.goml b/src/test/rustdoc-gui/sidebar.goml index c8ebb8c56f5..62dc76a40bc 100644 --- a/src/test/rustdoc-gui/sidebar.goml +++ b/src/test/rustdoc-gui/sidebar.goml @@ -7,12 +7,13 @@ assert-text: (".sidebar-elems > #all-types", "See all test_docs's items") assert-text: (".sidebar-elems > .crate > ul > li > a.current", "test_docs") // And we're also supposed to have the list of items in the current module. assert-text: (".sidebar-elems > .items > ul > li:nth-child(1)", "Modules") -assert-text: (".sidebar-elems > .items > ul > li:nth-child(2)", "Structs") -assert-text: (".sidebar-elems > .items > ul > li:nth-child(3)", "Enums") -assert-text: (".sidebar-elems > .items > ul > li:nth-child(4)", "Traits") -assert-text: (".sidebar-elems > .items > ul > li:nth-child(5)", "Functions") -assert-text: (".sidebar-elems > .items > ul > li:nth-child(6)", "Type Definitions") -assert-text: (".sidebar-elems > .items > ul > li:nth-child(7)", "Keywords") +assert-text: (".sidebar-elems > .items > ul > li:nth-child(2)", "Macros") +assert-text: (".sidebar-elems > .items > ul > li:nth-child(3)", "Structs") +assert-text: (".sidebar-elems > .items > ul > li:nth-child(4)", "Enums") +assert-text: (".sidebar-elems > .items > ul > li:nth-child(5)", "Traits") +assert-text: (".sidebar-elems > .items > ul > li:nth-child(6)", "Functions") +assert-text: (".sidebar-elems > .items > ul > li:nth-child(7)", "Type Definitions") +assert-text: (".sidebar-elems > .items > ul > li:nth-child(8)", "Keywords") assert-text: ("#structs + .item-table .item-left > a", "Foo") click: "#structs + .item-table .item-left > a" diff --git a/src/test/rustdoc-gui/src/lib2/lib.rs b/src/test/rustdoc-gui/src/lib2/lib.rs index d5835b78d2f..f2e76b546c4 100644 --- a/src/test/rustdoc-gui/src/lib2/lib.rs +++ b/src/test/rustdoc-gui/src/lib2/lib.rs @@ -84,3 +84,20 @@ pub mod summary_table { /// | content | content | pub struct Foo; } + +pub mod too_long { +pub type ReallyLongTypeNameLongLongLong = Option<unsafe extern "C" fn(a: *const u8, b: *const u8) -> *const u8>; + +pub const ReallyLongTypeNameLongLongLongConstBecauseWhyNotAConstRightGigaGigaSupraLong: u32 = 0; + +pub struct SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName { + pub a: u32, +} + +impl SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName { + /// ``` + /// let x = SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName { a: 0 }; + /// ``` + pub fn foo(&self) {} + } +} diff --git a/src/test/rustdoc-gui/src/test_docs/lib.rs b/src/test/rustdoc-gui/src/test_docs/lib.rs index 2a147e64d8b..652308a71cb 100644 --- a/src/test/rustdoc-gui/src/test_docs/lib.rs +++ b/src/test/rustdoc-gui/src/test_docs/lib.rs @@ -12,6 +12,7 @@ use std::fmt; /// /// ``` /// println!("nothing fancy"); +/// println!("but with two lines!"); /// ``` /// /// A failing to compile one: @@ -123,3 +124,10 @@ pub mod huge_amount_of_consts { /// Very long code text `hereIgoWithLongTextBecauseWhyNotAndWhyWouldntI`. pub mod long_code_block {} + +#[macro_export] +macro_rules! repro { + () => {}; +} + +pub use crate::repro as repro2; diff --git a/src/test/rustdoc-gui/type-declation-overflow.goml b/src/test/rustdoc-gui/type-declation-overflow.goml index 0a316e220a4..63ab867fb17 100644 --- a/src/test/rustdoc-gui/type-declation-overflow.goml +++ b/src/test/rustdoc-gui/type-declation-overflow.goml @@ -1,8 +1,25 @@ -// This test ensures that the type declaration content overflow is handled inside the <pre> directly. +// This test ensures that the items declaration content overflow is handled inside the <pre> directly. goto: file://|DOC_PATH|/lib2/long_trait/trait.ALongNameBecauseItHelpsTestingTheCurrentProblem.html // We set a fixed size so there is no chance of "random" resize. size: (1100, 800) // Logically, the <body> scroll width should be the width of the window. assert-property: ("body", {"scrollWidth": "1100"}) // However, since there is overflow in the type declaration, its scroll width is bigger. -assert-property: (".type-decl pre", {"scrollWidth": "1324"}) +assert-property: (".item-decl pre", {"scrollWidth": "1324"}) + +// We now make the same check on type declaration... +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"}) +// And now checking that it has scrollable content. +assert-property: (".item-decl pre", {"scrollWidth": "1103"}) + +// ... and constant. +// On a sidenote, it also checks that the (very) long title isn't changing the docblock width. +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"}) +// And now checking that it has scrollable content. +assert-property: (".item-decl pre", {"scrollWidth": "950"}) diff --git a/src/test/rustdoc-ui/doc-without-codeblock.rs b/src/test/rustdoc-ui/doc-without-codeblock.rs index 6812a454157..315fca19587 100644 --- a/src/test/rustdoc-ui/doc-without-codeblock.rs +++ b/src/test/rustdoc-ui/doc-without-codeblock.rs @@ -11,3 +11,12 @@ pub mod foo { //~^ ERROR missing code example in this documentation pub fn bar() {} } + +// This impl is here to ensure the lint isn't emitted for foreign traits implementations. +impl std::ops::Neg for Foo { + type Output = Self; + + fn neg(self) -> Self::Output { + Self + } +} diff --git a/src/test/rustdoc-ui/doc-without-codeblock.stderr b/src/test/rustdoc-ui/doc-without-codeblock.stderr index aac537e9783..1c138044165 100644 --- a/src/test/rustdoc-ui/doc-without-codeblock.stderr +++ b/src/test/rustdoc-ui/doc-without-codeblock.stderr @@ -6,7 +6,7 @@ LL | | LL | | /// Some docs. LL | | ... | -LL | | pub fn bar() {} +LL | | } LL | | } | |_^ | diff --git a/src/test/rustdoc/attributes.rs b/src/test/rustdoc/attributes.rs index 6a588fbd56e..1c7f4b72418 100644 --- a/src/test/rustdoc/attributes.rs +++ b/src/test/rustdoc/attributes.rs @@ -8,6 +8,6 @@ pub extern "C" fn f() {} #[export_name = "bar"] pub extern "C" fn g() {} -// @has foo/struct.Repr.html '//*[@class="docblock type-decl"]' '#[repr(C, align(8))]' +// @has foo/struct.Repr.html '//*[@class="docblock item-decl"]' '#[repr(C, align(8))]' #[repr(C, align(8))] pub struct Repr; diff --git a/src/test/rustdoc/reexports-priv.rs b/src/test/rustdoc/reexports-priv.rs index ff7424033aa..509457f6c96 100644 --- a/src/test/rustdoc/reexports-priv.rs +++ b/src/test/rustdoc/reexports-priv.rs @@ -5,25 +5,25 @@ extern crate reexports; -// @has 'foo/macro.addr_of.html' '//*[@class="docblock type-decl"]' 'pub macro addr_of($place : expr) {' +// @has 'foo/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place : expr) {' pub use reexports::addr_of; -// @has 'foo/macro.addr_of_crate.html' '//*[@class="docblock type-decl"]' 'pub(crate) macro addr_of_crate($place : expr) {' +// @has 'foo/macro.addr_of_crate.html' '//*[@class="docblock item-decl"]' 'pub(crate) macro addr_of_crate($place : expr) {' pub(crate) use reexports::addr_of_crate; -// @has 'foo/macro.addr_of_self.html' '//*[@class="docblock type-decl"]' 'pub(crate) macro addr_of_self($place : expr) {' +// @has 'foo/macro.addr_of_self.html' '//*[@class="docblock item-decl"]' 'pub(crate) macro addr_of_self($place : expr) {' pub(self) use reexports::addr_of_self; -// @has 'foo/struct.Foo.html' '//*[@class="docblock type-decl"]' 'pub struct Foo;' +// @has 'foo/struct.Foo.html' '//*[@class="docblock item-decl"]' 'pub struct Foo;' pub use reexports::Foo; -// @has 'foo/struct.FooCrate.html' '//*[@class="docblock type-decl"]' 'pub(crate) struct FooCrate;' +// @has 'foo/struct.FooCrate.html' '//*[@class="docblock item-decl"]' 'pub(crate) struct FooCrate;' pub(crate) use reexports::FooCrate; -// @has 'foo/struct.FooSelf.html' '//*[@class="docblock type-decl"]' 'pub(crate) struct FooSelf;' +// @has 'foo/struct.FooSelf.html' '//*[@class="docblock item-decl"]' 'pub(crate) struct FooSelf;' pub(self) use reexports::FooSelf; -// @has 'foo/enum.Bar.html' '//*[@class="docblock type-decl"]' 'pub enum Bar {' +// @has 'foo/enum.Bar.html' '//*[@class="docblock item-decl"]' 'pub enum Bar {' pub use reexports::Bar; -// @has 'foo/enum.BarCrate.html' '//*[@class="docblock type-decl"]' 'pub(crate) enum BarCrate {' +// @has 'foo/enum.BarCrate.html' '//*[@class="docblock item-decl"]' 'pub(crate) enum BarCrate {' pub(crate) use reexports::BarCrate; -// @has 'foo/enum.BarSelf.html' '//*[@class="docblock type-decl"]' 'pub(crate) enum BarSelf {' +// @has 'foo/enum.BarSelf.html' '//*[@class="docblock item-decl"]' 'pub(crate) enum BarSelf {' pub(self) use reexports::BarSelf; // @has 'foo/fn.foo.html' '//*[@class="rust fn"]' 'pub fn foo()' @@ -40,11 +40,11 @@ pub(crate) use reexports::TypeCrate; // @has 'foo/type.TypeSelf.html' '//*[@class="rust typedef"]' 'pub(crate) type TypeSelf =' pub(self) use reexports::TypeSelf; -// @has 'foo/union.Union.html' '//*[@class="docblock type-decl"]' 'pub union Union {' +// @has 'foo/union.Union.html' '//*[@class="docblock item-decl"]' 'pub union Union {' pub use reexports::Union; -// @has 'foo/union.UnionCrate.html' '//*[@class="docblock type-decl"]' 'pub(crate) union UnionCrate {' +// @has 'foo/union.UnionCrate.html' '//*[@class="docblock item-decl"]' 'pub(crate) union UnionCrate {' pub(crate) use reexports::UnionCrate; -// @has 'foo/union.UnionSelf.html' '//*[@class="docblock type-decl"]' 'pub(crate) union UnionSelf {' +// @has 'foo/union.UnionSelf.html' '//*[@class="docblock item-decl"]' 'pub(crate) union UnionSelf {' pub(self) use reexports::UnionSelf; pub mod foo { diff --git a/src/test/rustdoc/reexports.rs b/src/test/rustdoc/reexports.rs index ab4c5bc7439..c308d0c2f05 100644 --- a/src/test/rustdoc/reexports.rs +++ b/src/test/rustdoc/reexports.rs @@ -4,21 +4,21 @@ extern crate reexports; -// @has 'foo/macro.addr_of.html' '//*[@class="docblock type-decl"]' 'pub macro addr_of($place : expr) {' +// @has 'foo/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place : expr) {' pub use reexports::addr_of; // @!has 'foo/macro.addr_of_crate.html' pub(crate) use reexports::addr_of_crate; // @!has 'foo/macro.addr_of_self.html' pub(self) use reexports::addr_of_self; -// @has 'foo/struct.Foo.html' '//*[@class="docblock type-decl"]' 'pub struct Foo;' +// @has 'foo/struct.Foo.html' '//*[@class="docblock item-decl"]' 'pub struct Foo;' pub use reexports::Foo; // @!has 'foo/struct.FooCrate.html' pub(crate) use reexports::FooCrate; // @!has 'foo/struct.FooSelf.html' pub(self) use reexports::FooSelf; -// @has 'foo/enum.Bar.html' '//*[@class="docblock type-decl"]' 'pub enum Bar {' +// @has 'foo/enum.Bar.html' '//*[@class="docblock item-decl"]' 'pub enum Bar {' pub use reexports::Bar; // @!has 'foo/enum.BarCrate.html' pub(crate) use reexports::BarCrate; @@ -39,7 +39,7 @@ pub(crate) use reexports::TypeCrate; // @!has 'foo/type.TypeSelf.html' pub(self) use reexports::TypeSelf; -// @has 'foo/union.Union.html' '//*[@class="docblock type-decl"]' 'pub union Union {' +// @has 'foo/union.Union.html' '//*[@class="docblock item-decl"]' 'pub union Union {' pub use reexports::Union; // @!has 'foo/union.UnionCrate.html' pub(crate) use reexports::UnionCrate; diff --git a/src/test/rustdoc/toggle-item-contents.rs b/src/test/rustdoc/toggle-item-contents.rs index ae871e79d7f..937646987dd 100644 --- a/src/test/rustdoc/toggle-item-contents.rs +++ b/src/test/rustdoc/toggle-item-contents.rs @@ -55,7 +55,7 @@ pub union Union { // @has 'toggle_item_contents/struct.PrivStruct.html' // @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0 -// @has - '//div[@class="docblock type-decl"]' 'fields omitted' +// @has - '//div[@class="docblock item-decl"]' 'fields omitted' pub struct PrivStruct { a: usize, b: usize, diff --git a/src/test/rustdoc/trait_alias.rs b/src/test/rustdoc/trait_alias.rs index 6cd4a1a0afa..c9fccf5a77c 100644 --- a/src/test/rustdoc/trait_alias.rs +++ b/src/test/rustdoc/trait_alias.rs @@ -13,11 +13,14 @@ use std::fmt::Debug; // @has foo/index.html '//a[@class="traitalias"]' 'Alias2' // @has foo/index.html '//a[@class="traitalias"]' 'Foo' -// @has foo/traitalias.CopyAlias.html '//section[@id="main"]/pre' 'trait CopyAlias = Copy;' +// @has foo/traitalias.CopyAlias.html +// @has - '//section[@id="main"]/div[@class="docblock item-decl"]/pre' 'trait CopyAlias = Copy;' pub trait CopyAlias = Copy; -// @has foo/traitalias.Alias2.html '//section[@id="main"]/pre' 'trait Alias2 = Copy + Debug;' +// @has foo/traitalias.Alias2.html +// @has - '//section[@id="main"]/div[@class="docblock item-decl"]/pre' 'trait Alias2 = Copy + Debug;' pub trait Alias2 = Copy + Debug; -// @has foo/traitalias.Foo.html '//section[@id="main"]/pre' 'trait Foo<T> = Into<T> + Debug;' +// @has foo/traitalias.Foo.html +// @has - '//section[@id="main"]/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 {} diff --git a/src/test/ui/consts/closure-structural-match-issue-90013.rs b/src/test/ui/consts/closure-structural-match-issue-90013.rs new file mode 100644 index 00000000000..7853ee41a90 --- /dev/null +++ b/src/test/ui/consts/closure-structural-match-issue-90013.rs @@ -0,0 +1,8 @@ +// Regression test for issue 90013. +// check-pass +#![allow(incomplete_features)] +#![feature(inline_const)] + +fn main() { + const { || {} }; +} diff --git a/src/test/ui/derives/derives-span-Hash-enum-struct-variant.stderr b/src/test/ui/derives/derives-span-Hash-enum-struct-variant.stderr index 89186817e09..47c7f1c2c33 100644 --- a/src/test/ui/derives/derives-span-Hash-enum-struct-variant.stderr +++ b/src/test/ui/derives/derives-span-Hash-enum-struct-variant.stderr @@ -11,7 +11,7 @@ note: required by a bound in `std::hash::Hash::hash` --> $SRC_DIR/core/src/hash/mod.rs:LL:COL | LL | fn hash<H: Hasher>(&self, state: &mut H); - | ^^^^^^ required by this bound in `std::hash::Hash::hash` + | ^ required by this bound in `std::hash::Hash::hash` = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Hash-enum.stderr b/src/test/ui/derives/derives-span-Hash-enum.stderr index 6abb7e78b13..92f084b58e3 100644 --- a/src/test/ui/derives/derives-span-Hash-enum.stderr +++ b/src/test/ui/derives/derives-span-Hash-enum.stderr @@ -11,7 +11,7 @@ note: required by a bound in `std::hash::Hash::hash` --> $SRC_DIR/core/src/hash/mod.rs:LL:COL | LL | fn hash<H: Hasher>(&self, state: &mut H); - | ^^^^^^ required by this bound in `std::hash::Hash::hash` + | ^ required by this bound in `std::hash::Hash::hash` = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Hash-struct.stderr b/src/test/ui/derives/derives-span-Hash-struct.stderr index 405285f8838..c57cebe04eb 100644 --- a/src/test/ui/derives/derives-span-Hash-struct.stderr +++ b/src/test/ui/derives/derives-span-Hash-struct.stderr @@ -11,7 +11,7 @@ note: required by a bound in `std::hash::Hash::hash` --> $SRC_DIR/core/src/hash/mod.rs:LL:COL | LL | fn hash<H: Hasher>(&self, state: &mut H); - | ^^^^^^ required by this bound in `std::hash::Hash::hash` + | ^ required by this bound in `std::hash::Hash::hash` = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/derives/derives-span-Hash-tuple-struct.stderr b/src/test/ui/derives/derives-span-Hash-tuple-struct.stderr index aa12314c051..200937f0c9f 100644 --- a/src/test/ui/derives/derives-span-Hash-tuple-struct.stderr +++ b/src/test/ui/derives/derives-span-Hash-tuple-struct.stderr @@ -11,7 +11,7 @@ note: required by a bound in `std::hash::Hash::hash` --> $SRC_DIR/core/src/hash/mod.rs:LL:COL | LL | fn hash<H: Hasher>(&self, state: &mut H); - | ^^^^^^ required by this bound in `std::hash::Hash::hash` + | ^ required by this bound in `std::hash::Hash::hash` = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0637.rs b/src/test/ui/error-codes/E0637.rs index b4888d4af6a..382ce3ed01f 100644 --- a/src/test/ui/error-codes/E0637.rs +++ b/src/test/ui/error-codes/E0637.rs @@ -1,9 +1,17 @@ -struct Foo<'a: '_>(&'a u8); //~ ERROR cannot be used here -fn foo<'a: '_>(_: &'a u8) {} //~ ERROR cannot be used here +fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { + //~^ ERROR: `'_` cannot be used here [E0637] + //~| ERROR: missing lifetime specifier + if str1.len() > str2.len() { + str1 + } else { + str2 + } +} -struct Bar<'a>(&'a u8); -impl<'a: '_> Bar<'a> { //~ ERROR cannot be used here - fn bar() {} +fn and_without_explicit_lifetime<T>() +where + T: Into<&u32>, //~ ERROR: `&` without an explicit lifetime name cannot be used here [E0637] +{ } fn main() {} diff --git a/src/test/ui/error-codes/E0637.stderr b/src/test/ui/error-codes/E0637.stderr index d19ebfd15a5..87aaba65a73 100644 --- a/src/test/ui/error-codes/E0637.stderr +++ b/src/test/ui/error-codes/E0637.stderr @@ -1,21 +1,28 @@ error[E0637]: `'_` cannot be used here - --> $DIR/E0637.rs:1:16 + --> $DIR/E0637.rs:1:24 | -LL | struct Foo<'a: '_>(&'a u8); - | ^^ `'_` is a reserved lifetime name +LL | fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { + | ^^ `'_` is a reserved lifetime name -error[E0637]: `'_` cannot be used here - --> $DIR/E0637.rs:2:12 +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/E0637.rs:13:13 | -LL | fn foo<'a: '_>(_: &'a u8) {} - | ^^ `'_` is a reserved lifetime name +LL | T: Into<&u32>, + | ^ explicit lifetime name needed here -error[E0637]: `'_` cannot be used here - --> $DIR/E0637.rs:5:10 +error[E0106]: missing lifetime specifier + --> $DIR/E0637.rs:1:62 + | +LL | fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { + | ------- ------- ^^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `str1` or `str2` +help: consider introducing a named lifetime parameter | -LL | impl<'a: '_> Bar<'a> { - | ^^ `'_` is a reserved lifetime name +LL | fn underscore_lifetime<'a, '_>(str1: &'a str, str2: &'a str) -> &'a str { + | +++ ~~ ~~ ~~ error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0637`. +Some errors have detailed explanations: E0106, E0637. +For more information about an error, try `rustc --explain E0106`. diff --git a/src/test/ui/generic-associated-types/issue-74816.stderr b/src/test/ui/generic-associated-types/issue-74816.stderr index d5cc5cfbe91..49ae87cbfe9 100644 --- a/src/test/ui/generic-associated-types/issue-74816.stderr +++ b/src/test/ui/generic-associated-types/issue-74816.stderr @@ -1,34 +1,34 @@ -error[E0277]: the size for values of type `Self` cannot be known at compilation time +error[E0277]: the trait bound `Self: Trait1` is not satisfied --> $DIR/issue-74816.rs:9:5 | LL | type Associated: Trait1 = Self; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait1` is not implemented for `Self` | note: required by a bound in `Trait2::Associated` - --> $DIR/issue-74816.rs:9:5 + --> $DIR/issue-74816.rs:9:22 | LL | type Associated: Trait1 = Self; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Trait2::Associated` + | ^^^^^^ required by this bound in `Trait2::Associated` help: consider further restricting `Self` | -LL | trait Trait2: Sized { - | +++++++ +LL | trait Trait2: Trait1 { + | ++++++++ -error[E0277]: the trait bound `Self: Trait1` is not satisfied +error[E0277]: the size for values of type `Self` cannot be known at compilation time --> $DIR/issue-74816.rs:9:5 | LL | type Associated: Trait1 = Self; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait1` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | note: required by a bound in `Trait2::Associated` - --> $DIR/issue-74816.rs:9:22 + --> $DIR/issue-74816.rs:9:5 | LL | type Associated: Trait1 = Self; - | ^^^^^^ required by this bound in `Trait2::Associated` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Trait2::Associated` help: consider further restricting `Self` | -LL | trait Trait2: Trait1 { - | ++++++++ +LL | trait Trait2: Sized { + | +++++++ error: aborting due to 2 previous errors diff --git a/src/test/ui/generic-associated-types/issue-86483.stderr b/src/test/ui/generic-associated-types/issue-86483.stderr index 5d0fcbca552..d6978794e1e 100644 --- a/src/test/ui/generic-associated-types/issue-86483.stderr +++ b/src/test/ui/generic-associated-types/issue-86483.stderr @@ -20,13 +20,13 @@ LL | for<'a> T: 'a, | ^^ error[E0311]: the parameter type `T` may not live long enough - --> $DIR/issue-86483.rs:9:19 + --> $DIR/issue-86483.rs:9:5 | LL | pub trait IceIce<T> | - help: consider adding an explicit lifetime bound...: `T: 'a` ... LL | type Ice<'v>: IntoIterator<Item = &'v T>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds... + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds... | note: ...that is required by this bound --> $DIR/issue-86483.rs:7:16 diff --git a/src/test/ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.stderr b/src/test/ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.stderr index 50f90618e4d..2c397d80b01 100644 --- a/src/test/ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.stderr +++ b/src/test/ui/generics/issue-61631-default-type-param-can-reference-self-in-trait.stderr @@ -6,10 +6,10 @@ LL | impl Tsized for () {} | = help: the trait `Sized` is not implemented for `[()]` note: required by a bound in `Tsized` - --> $DIR/issue-61631-default-type-param-can-reference-self-in-trait.rs:17:17 + --> $DIR/issue-61631-default-type-param-can-reference-self-in-trait.rs:17:14 | LL | trait Tsized<P: Sized = [Self]> {} - | ^^^^^ required by this bound in `Tsized` + | ^ required by this bound in `Tsized` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-16966.stderr b/src/test/ui/issues/issue-16966.stderr index 7597824e08f..09e20c0c777 100644 --- a/src/test/ui/issues/issue-16966.stderr +++ b/src/test/ui/issues/issue-16966.stderr @@ -1,21 +1,11 @@ -error[E0283]: type annotations needed +error[E0282]: type annotations needed --> $DIR/issue-16966.rs:2:5 | LL | panic!(std::default::Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `M` declared on the function `begin_panic` | - = note: cannot satisfy `_: Any` -note: required by a bound in `begin_panic` - --> $SRC_DIR/std/src/panicking.rs:LL:COL - | -LL | pub fn begin_panic<M: Any + Send>(msg: M) -> ! { - | ^^^ required by this bound in `begin_panic` = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider specifying the type argument in the function call - | -LL | $crate::rt::begin_panic::<M>($msg) - | +++++ error: aborting due to previous error -For more information about this error, try `rustc --explain E0283`. +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/issues/issue-21160.stderr b/src/test/ui/issues/issue-21160.stderr index c2f6fc21acd..92742b50619 100644 --- a/src/test/ui/issues/issue-21160.stderr +++ b/src/test/ui/issues/issue-21160.stderr @@ -10,7 +10,7 @@ note: required by a bound in `std::hash::Hash::hash` --> $SRC_DIR/core/src/hash/mod.rs:LL:COL | LL | fn hash<H: Hasher>(&self, state: &mut H); - | ^^^^^^ required by this bound in `std::hash::Hash::hash` + | ^ required by this bound in `std::hash::Hash::hash` = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/issues/issue-23122-2.stderr b/src/test/ui/issues/issue-23122-2.stderr index e6cec722978..b345e901787 100644 --- a/src/test/ui/issues/issue-23122-2.stderr +++ b/src/test/ui/issues/issue-23122-2.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<T as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: Next` +error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<T as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: Sized` --> $DIR/issue-23122-2.rs:9:17 | LL | type Next = <GetNext<T::Next> as Next>::Next; diff --git a/src/test/ui/issues/issue-54954.stderr b/src/test/ui/issues/issue-54954.stderr index d88397fd7e1..df76a985559 100644 --- a/src/test/ui/issues/issue-54954.stderr +++ b/src/test/ui/issues/issue-54954.stderr @@ -12,10 +12,10 @@ LL | const ARR_LEN: usize = Tt::const_val::<[i8; 123]>(); | = note: cannot satisfy `_: Tt` note: required by a bound in `Tt::const_val` - --> $DIR/issue-54954.rs:5:27 + --> $DIR/issue-54954.rs:5:24 | LL | const fn const_val<T: Sized>() -> usize { - | ^^^^^ required by this bound in `Tt::const_val` + | ^ required by this bound in `Tt::const_val` error: aborting due to 2 previous errors diff --git a/src/test/ui/macros/missing-bang-in-decl.fixed b/src/test/ui/macros/missing-bang-in-decl.fixed new file mode 100644 index 00000000000..b1aa3298bfa --- /dev/null +++ b/src/test/ui/macros/missing-bang-in-decl.fixed @@ -0,0 +1,16 @@ +// run-rustfix + +#![allow(unused_macros)] + +macro_rules! foo { + //~^ ERROR expected `!` after `macro_rules` + () => {}; +} + +macro_rules! bar { + //~^ ERROR expected `!` after `macro_rules` + //~^^ ERROR macro names aren't followed by a `!` + () => {}; +} + +fn main() {} diff --git a/src/test/ui/macros/missing-bang-in-decl.rs b/src/test/ui/macros/missing-bang-in-decl.rs new file mode 100644 index 00000000000..8393f15fc52 --- /dev/null +++ b/src/test/ui/macros/missing-bang-in-decl.rs @@ -0,0 +1,16 @@ +// run-rustfix + +#![allow(unused_macros)] + +macro_rules foo { + //~^ ERROR expected `!` after `macro_rules` + () => {}; +} + +macro_rules bar! { + //~^ ERROR expected `!` after `macro_rules` + //~^^ ERROR macro names aren't followed by a `!` + () => {}; +} + +fn main() {} diff --git a/src/test/ui/macros/missing-bang-in-decl.stderr b/src/test/ui/macros/missing-bang-in-decl.stderr new file mode 100644 index 00000000000..dfabafb0a7a --- /dev/null +++ b/src/test/ui/macros/missing-bang-in-decl.stderr @@ -0,0 +1,20 @@ +error: expected `!` after `macro_rules` + --> $DIR/missing-bang-in-decl.rs:5:1 + | +LL | macro_rules foo { + | ^^^^^^^^^^^ help: add a `!`: `macro_rules!` + +error: expected `!` after `macro_rules` + --> $DIR/missing-bang-in-decl.rs:10:1 + | +LL | macro_rules bar! { + | ^^^^^^^^^^^ help: add a `!`: `macro_rules!` + +error: macro names aren't followed by a `!` + --> $DIR/missing-bang-in-decl.rs:10:16 + | +LL | macro_rules bar! { + | ^ help: remove the `!` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/pattern/non-structural-match-types.rs b/src/test/ui/pattern/non-structural-match-types.rs new file mode 100644 index 00000000000..713418fc5b2 --- /dev/null +++ b/src/test/ui/pattern/non-structural-match-types.rs @@ -0,0 +1,14 @@ +// edition:2021 +#![allow(incomplete_features)] +#![allow(unreachable_code)] +#![feature(const_async_blocks)] +#![feature(inline_const)] + +fn main() { + match loop {} { + const { || {} } => {}, //~ ERROR cannot be used in patterns + } + match loop {} { + const { async {} } => {}, //~ ERROR cannot be used in patterns + } +} diff --git a/src/test/ui/pattern/non-structural-match-types.stderr b/src/test/ui/pattern/non-structural-match-types.stderr new file mode 100644 index 00000000000..91fed81eaef --- /dev/null +++ b/src/test/ui/pattern/non-structural-match-types.stderr @@ -0,0 +1,14 @@ +error: `[closure@$DIR/non-structural-match-types.rs:9:17: 9:22]` cannot be used in patterns + --> $DIR/non-structural-match-types.rs:9:9 + | +LL | const { || {} } => {}, + | ^^^^^^^^^^^^^^^ + +error: `impl Future` cannot be used in patterns + --> $DIR/non-structural-match-types.rs:12:9 + | +LL | const { async {} } => {}, + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr b/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr index 4a4544c16c9..fffb91f9870 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr @@ -20,11 +20,11 @@ error[E0277]: the trait bound `T: Bar` is not satisfied LL | T::c::<T>(); | ^^^^^^^^^ the trait `Bar` is not implemented for `T` | -note: required by a bound in `Foo::c` - --> $DIR/trait-where-clause.rs:9:10 +note: required by `Foo::c` + --> $DIR/trait-where-clause.rs:9:5 | LL | fn c<T: ~const Bar>(); - | ^ required by this bound in `Foo::c` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider further restricting this bound | LL | const fn test1<T: ~const Foo + Bar + Bar>() { @@ -52,11 +52,11 @@ error[E0277]: the trait bound `T: Bar` is not satisfied LL | T::c::<T>(); | ^^^^^^^^^ the trait `Bar` is not implemented for `T` | -note: required by a bound in `Foo::c` - --> $DIR/trait-where-clause.rs:9:10 +note: required by `Foo::c` + --> $DIR/trait-where-clause.rs:9:5 | LL | fn c<T: ~const Bar>(); - | ^ required by this bound in `Foo::c` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider further restricting this bound | LL | fn test3<T: Foo + Bar>() { diff --git a/src/test/ui/rustc-rust-log.rs b/src/test/ui/rustc-rust-log.rs index 8ceb24dd2af..52e7dcf4499 100644 --- a/src/test/ui/rustc-rust-log.rs +++ b/src/test/ui/rustc-rust-log.rs @@ -1,9 +1,6 @@ // run-pass // This test is just checking that we won't ICE if logging is turned -// on; don't bother trying to compare that (copious) output. (Note -// also that this test potentially silly, since we do not build+test -// debug versions of rustc as part of our continuous integration -// process...) +// on; don't bother trying to compare that (copious) output. // // dont-check-compiler-stdout // dont-check-compiler-stderr diff --git a/src/test/ui/suggestions/issue-84973-blacklist.stderr b/src/test/ui/suggestions/issue-84973-blacklist.stderr index 58075ed7cae..ae55c96702a 100644 --- a/src/test/ui/suggestions/issue-84973-blacklist.stderr +++ b/src/test/ui/suggestions/issue-84973-blacklist.stderr @@ -49,10 +49,10 @@ LL | f_sized(*ref_cl); | = help: the trait `Sized` is not implemented for `dyn Fn()` note: required by a bound in `f_sized` - --> $DIR/issue-84973-blacklist.rs:9:15 + --> $DIR/issue-84973-blacklist.rs:9:12 | LL | fn f_sized<T: Sized>(t: T) {} - | ^^^^^ required by this bound in `f_sized` + | ^ required by this bound in `f_sized` error[E0277]: `Rc<{integer}>` cannot be sent between threads safely --> $DIR/issue-84973-blacklist.rs:27:12 diff --git a/src/test/ui/suggestions/slice-issue-87994.stderr b/src/test/ui/suggestions/slice-issue-87994.stderr index 9e0d4ced011..0275fd475d8 100644 --- a/src/test/ui/suggestions/slice-issue-87994.stderr +++ b/src/test/ui/suggestions/slice-issue-87994.stderr @@ -1,4 +1,4 @@ -error[E0277]: `[i32]` is not an iterator +error[E0277]: the size for values of type `[i32]` cannot be known at compilation time --> $DIR/slice-issue-87994.rs:3:12 | LL | for _ in v[1..] { @@ -18,7 +18,7 @@ LL | for _ in &v[1..] { LL | for _ in &mut v[1..] { | ++++ -error[E0277]: the size for values of type `[i32]` cannot be known at compilation time +error[E0277]: `[i32]` is not an iterator --> $DIR/slice-issue-87994.rs:3:12 | LL | for _ in v[1..] { @@ -38,7 +38,7 @@ LL | for _ in &v[1..] { LL | for _ in &mut v[1..] { | ++++ -error[E0277]: `[K]` is not an iterator +error[E0277]: the size for values of type `[K]` cannot be known at compilation time --> $DIR/slice-issue-87994.rs:11:13 | LL | for i2 in v2[1..] { @@ -58,7 +58,7 @@ LL | for i2 in &v2[1..] { LL | for i2 in &mut v2[1..] { | ++++ -error[E0277]: the size for values of type `[K]` cannot be known at compilation time +error[E0277]: `[K]` is not an iterator --> $DIR/slice-issue-87994.rs:11:13 | LL | for i2 in v2[1..] { diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.rs index 4baf198b12f..74708193317 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.rs @@ -5,7 +5,7 @@ use std::fmt::Debug; fn main() {} type Two<A, B> = impl Debug; -//~^ ERROR the trait bound `A: Foo` is not satisfied +//~^ ERROR the trait bound `A: Foo` is not satisfied in `(A, B, <A as Foo>::Bar)` //~| ERROR `A` doesn't implement `Debug` //~| ERROR `B` doesn't implement `Debug` diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr index f21e036edc2..a8eb53a50e3 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr @@ -10,6 +10,18 @@ note: previous use here LL | fn two<T: Debug + Foo, U: Debug>(t: T, u: U) -> Two<T, U> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error[E0277]: the trait bound `A: Foo` is not satisfied in `(A, B, <A as Foo>::Bar)` + --> $DIR/generic_duplicate_param_use9.rs:7:18 + | +LL | type Two<A, B> = impl Debug; + | ^^^^^^^^^^ within `(A, B, <A as Foo>::Bar)`, the trait `Foo` is not implemented for `A` + | + = note: required because it appears within the type `(A, B, <A as Foo>::Bar)` +help: consider restricting type parameter `A` + | +LL | type Two<A: Foo, B> = impl Debug; + | +++++ + error[E0277]: `A` doesn't implement `Debug` --> $DIR/generic_duplicate_param_use9.rs:7:18 | @@ -34,18 +46,6 @@ help: consider restricting type parameter `B` LL | type Two<A, B: std::fmt::Debug> = impl Debug; | +++++++++++++++++ -error[E0277]: the trait bound `A: Foo` is not satisfied - --> $DIR/generic_duplicate_param_use9.rs:7:18 - | -LL | type Two<A, B> = impl Debug; - | ^^^^^^^^^^ the trait `Foo` is not implemented for `A` - | - = note: required because of the requirements on the impl of `Debug` for `(A, B, <A as Foo>::Bar)` -help: consider restricting type parameter `A` - | -LL | type Two<A: Foo, B> = impl Debug; - | +++++ - error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/typeck/issue-89935.rs b/src/test/ui/typeck/issue-89935.rs new file mode 100644 index 00000000000..03f8f09a722 --- /dev/null +++ b/src/test/ui/typeck/issue-89935.rs @@ -0,0 +1,18 @@ +// check-pass + +trait Foo: Baz {} +trait Bar {} +trait Baz: Bar { + fn bar(&self); +} + +impl<T: Foo> Bar for T {} +impl<T: Foo> Baz for T { + fn bar(&self) {} +} + +fn accept_foo(x: Box<dyn Foo>) { + x.bar(); +} + +fn main() {} diff --git a/src/test/ui/unique-object-noncopyable.stderr b/src/test/ui/unique-object-noncopyable.stderr index 8626b726f47..5c40787febf 100644 --- a/src/test/ui/unique-object-noncopyable.stderr +++ b/src/test/ui/unique-object-noncopyable.stderr @@ -19,10 +19,10 @@ LL | | >(Unique<T>, A); | |________________- doesn't satisfy `Box<dyn Foo>: Clone` | = note: the following trait bounds were not satisfied: - `dyn Foo: Clone` - which is required by `Box<dyn Foo>: Clone` `dyn Foo: Sized` which is required by `Box<dyn Foo>: Clone` + `dyn Foo: Clone` + which is required by `Box<dyn Foo>: Clone` error: aborting due to previous error diff --git a/src/test/ui/unsized/unsized-bare-typaram.stderr b/src/test/ui/unsized/unsized-bare-typaram.stderr index 0dd439e14e3..531e9b4c9c9 100644 --- a/src/test/ui/unsized/unsized-bare-typaram.stderr +++ b/src/test/ui/unsized/unsized-bare-typaram.stderr @@ -7,10 +7,10 @@ LL | fn foo<T: ?Sized>() { bar::<T>() } | this type parameter needs to be `std::marker::Sized` | note: required by a bound in `bar` - --> $DIR/unsized-bare-typaram.rs:1:11 + --> $DIR/unsized-bare-typaram.rs:1:8 | LL | fn bar<T: Sized>() { } - | ^^^^^ required by this bound in `bar` + | ^ required by this bound in `bar` help: consider removing the `?Sized` bound to make the type parameter `Sized` | LL - fn foo<T: ?Sized>() { bar::<T>() } diff --git a/src/test/ui/unsized/unsized-struct.stderr b/src/test/ui/unsized/unsized-struct.stderr index 88ba7567402..1c70a840c77 100644 --- a/src/test/ui/unsized/unsized-struct.stderr +++ b/src/test/ui/unsized/unsized-struct.stderr @@ -38,10 +38,10 @@ note: required because it appears within the type `Bar<T>` LL | struct Bar<T: ?Sized> { data: T } | ^^^ note: required by a bound in `is_sized` - --> $DIR/unsized-struct.rs:1:15 + --> $DIR/unsized-struct.rs:1:13 | LL | fn is_sized<T:Sized>() { } - | ^^^^^ required by this bound in `is_sized` + | ^ required by this bound in `is_sized` help: consider removing the `?Sized` bound to make the type parameter `Sized` | LL - fn bar2<T: ?Sized>() { is_sized::<Bar<T>>() } diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index e700e5f0d73..3b4c687209e 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -2730,11 +2730,13 @@ Released 2018-09-13 [`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons [`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast +[`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any [`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation [`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map [`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref +[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into [`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10 @@ -2836,6 +2838,7 @@ Released 2018-09-13 [`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok [`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms [`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding +[`match_str_case_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_str_case_mismatch [`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm [`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants [`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter @@ -2896,6 +2899,7 @@ Released 2018-09-13 [`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self [`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect +[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty @@ -3012,16 +3016,19 @@ Released 2018-09-13 [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some [`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display +[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines [`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg +[`trailing_empty_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_empty_array [`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds [`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str [`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool [`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char [`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float +[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes [`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr [`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref [`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts @@ -3031,10 +3038,12 @@ Released 2018-09-13 [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds +[`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init +[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp [`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index ba3ed3053ac..ed7fb144013 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.57" +version = "0.1.58" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index 4a13a452409..affb283017c 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] bytecount = "0.6" clap = "2.33" +indoc = "1.0" itertools = "0.10" opener = "0.5" regex = "1.5" diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 8fdeba9842a..b5c04efce3b 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -28,6 +28,7 @@ fn main() { matches.value_of("pass"), matches.value_of("name"), matches.value_of("category"), + matches.is_present("msrv"), ) { Ok(_) => update_lints::run(update_lints::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {}", e), @@ -147,6 +148,11 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { "internal_warn", ]) .takes_value(true), + ) + .arg( + Arg::with_name("msrv") + .long("msrv") + .help("Add MSRV config code to the lint"), ), ) .subcommand( diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 3a81aaba6de..25320907bb4 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -1,4 +1,5 @@ use crate::clippy_project_root; +use indoc::indoc; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::io::{self, ErrorKind}; @@ -32,7 +33,7 @@ impl<T> Context for io::Result<T> { /// # Errors /// /// This function errors out if the files couldn't be created or written to. -pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> io::Result<()> { +pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>, msrv: bool) -> io::Result<()> { let lint = LintData { pass: pass.expect("`pass` argument is validated by clap"), name: lint_name.expect("`name` argument is validated by clap"), @@ -40,29 +41,12 @@ pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str project_root: clippy_project_root(), }; - create_lint(&lint).context("Unable to create lint implementation")?; + create_lint(&lint, msrv).context("Unable to create lint implementation")?; create_test(&lint).context("Unable to create a test for the new lint") } -fn create_lint(lint: &LintData<'_>) -> io::Result<()> { - let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass { - "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), - "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"), - _ => { - unreachable!("`pass_type` should only ever be `early` or `late`!"); - }, - }; - - let camel_case_name = to_camel_case(lint.name); - let lint_contents = get_lint_file_contents( - pass_type, - pass_lifetimes, - lint.name, - &camel_case_name, - lint.category, - pass_import, - context_import, - ); +fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { + let lint_contents = get_lint_file_contents(lint, enable_msrv); let lint_path = format!("clippy_lints/src/{}.rs", lint.name); write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes()) @@ -122,12 +106,13 @@ fn to_camel_case(name: &str) -> String { fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { let mut contents = format!( - "#![warn(clippy::{})] + indoc! {" + #![warn(clippy::{})] -fn main() {{ - // test code goes here -}} -", + fn main() {{ + // test code goes here + }} + "}, lint_name ); @@ -140,64 +125,143 @@ fn main() {{ fn get_manifest_contents(lint_name: &str, hint: &str) -> String { format!( - r#" -# {} + indoc! {r#" + # {} -[package] -name = "{}" -version = "0.1.0" -publish = false + [package] + name = "{}" + version = "0.1.0" + publish = false -[workspace] -"#, + [workspace] + "#}, hint, lint_name ) } -fn get_lint_file_contents( - pass_type: &str, - pass_lifetimes: &str, - lint_name: &str, - camel_case_name: &str, - category: &str, - pass_import: &str, - context_import: &str, -) -> String { - format!( - "use rustc_lint::{{{type}, {context_import}}}; -use rustc_session::{{declare_lint_pass, declare_tool_lint}}; -{pass_import} - -declare_clippy_lint! {{ - /// ### What it does - /// - /// ### Why is this bad? - /// - /// ### Example - /// ```rust - /// // example code where clippy issues a warning - /// ``` - /// Use instead: - /// ```rust - /// // example code which does not raise clippy warning - /// ``` - pub {name_upper}, - {category}, - \"default lint description\" -}} - -declare_lint_pass!({name_camel} => [{name_upper}]); - -impl {type}{lifetimes} for {name_camel} {{}} -", - type=pass_type, - lifetimes=pass_lifetimes, - name_upper=lint_name.to_uppercase(), - name_camel=camel_case_name, - category=category, - pass_import=pass_import, - context_import=context_import - ) +fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { + let mut result = String::new(); + + let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass { + "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), + "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"), + _ => { + unreachable!("`pass_type` should only ever be `early` or `late`!"); + }, + }; + + let lint_name = lint.name; + let pass_name = lint.pass; + let category = lint.category; + let name_camel = to_camel_case(lint.name); + let name_upper = lint_name.to_uppercase(); + + result.push_str(&if enable_msrv { + format!( + indoc! {" + use clippy_utils::msrvs; + {pass_import} + use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; + use rustc_semver::RustcVersion; + use rustc_session::{{declare_tool_lint, impl_lint_pass}}; + + "}, + pass_type = pass_type, + pass_import = pass_import, + context_import = context_import, + ) + } else { + format!( + indoc! {" + {pass_import} + use rustc_lint::{{{context_import}, {pass_type}}}; + use rustc_session::{{declare_lint_pass, declare_tool_lint}}; + + "}, + pass_import = pass_import, + pass_type = pass_type, + context_import = context_import + ) + }); + + result.push_str(&format!( + indoc! {" + declare_clippy_lint! {{ + /// ### What it does + /// + /// ### Why is this bad? + /// + /// ### Example + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub {name_upper}, + {category}, + \"default lint description\" + }} + "}, + name_upper = name_upper, + category = category, + )); + + result.push_str(&if enable_msrv { + format!( + indoc! {" + pub struct {name_camel} {{ + msrv: Option<RustcVersion>, + }} + + impl {name_camel} {{ + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self {{ + Self {{ msrv }} + }} + }} + + impl_lint_pass!({name_camel} => [{name_upper}]); + + impl {pass_type}{pass_lifetimes} for {name_camel} {{ + extract_msrv_attr!({context_import}); + }} + + // TODO: Register the lint pass in `clippy_lints/src/lib.rs`, + // e.g. store.register_{pass_name}_pass(move || Box::new({module_name}::{name_camel}::new(msrv))); + // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. + // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`. + // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` + "}, + pass_type = pass_type, + pass_lifetimes = pass_lifetimes, + pass_name = pass_name, + name_upper = name_upper, + name_camel = name_camel, + module_name = lint_name, + context_import = context_import, + ) + } else { + format!( + indoc! {" + declare_lint_pass!({name_camel} => [{name_upper}]); + + impl {pass_type}{pass_lifetimes} for {name_camel} {{}} + // + // TODO: Register the lint pass in `clippy_lints/src/lib.rs`, + // e.g. store.register_{pass_name}_pass(|| Box::new({module_name}::{name_camel})); + "}, + pass_type = pass_type, + pass_lifetimes = pass_lifetimes, + pass_name = pass_name, + name_upper = name_upper, + name_camel = name_camel, + module_name = lint_name, + ) + }); + + result } #[test] diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 10d859988f6..23f58bc4915 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -619,8 +619,8 @@ mod tests { Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"), ]; let expected = vec![ - format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK.to_string()), - format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string()), + format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK), + format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK), ]; assert_eq!(expected, gen_changelog_lint_list(lints.iter())); } diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 7900dc6d041..aaf9ac83d49 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.57" +version = "0.1.58" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs new file mode 100644 index 00000000000..03621887a34 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs @@ -0,0 +1,34 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +use super::FN_TO_NUMERIC_CAST_ANY; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + // We allow casts from any function type to any function type. + match cast_to.kind() { + ty::FnDef(..) | ty::FnPtr(..) => return, + _ => { /* continue to checks */ }, + } + + match cast_from.kind() { + ty::FnDef(..) | ty::FnPtr(_) => { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); + + span_lint_and_sugg( + cx, + FN_TO_NUMERIC_CAST_ANY, + expr.span, + &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), + "did you mean to invoke the function?", + format!("{}() as {}", from_snippet, cast_to), + applicability, + ); + }, + _ => {}, + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index 27e1bea7993..f0800c6a6f1 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -7,6 +7,7 @@ mod cast_ref_to_mut; mod cast_sign_loss; mod char_lit_as_u8; mod fn_to_numeric_cast; +mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; mod ptr_as_ptr; mod unnecessary_cast; @@ -253,6 +254,42 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does + /// Checks for casts of a function pointer to any integer type. + /// + /// ### Why is this bad? + /// Casting a function pointer to an integer can have surprising results and can occur + /// accidentally if parantheses are omitted from a function call. If you aren't doing anything + /// low-level with function pointers then you can opt-out of casting functions to integers in + /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function + /// pointer casts in your code. + /// + /// ### Example + /// ```rust + /// // Bad: fn1 is cast as `usize` + /// fn fn1() -> u16 { + /// 1 + /// }; + /// let _ = fn1 as usize; + /// + /// // Good: maybe you intended to call the function? + /// fn fn2() -> u16 { + /// 1 + /// }; + /// let _ = fn2() as usize; + /// + /// // Good: maybe you intended to cast it to a function type? + /// fn fn3() -> u16 { + /// 1 + /// } + /// let _ = fn3 as fn() -> u16; + /// ``` + pub FN_TO_NUMERIC_CAST_ANY, + restriction, + "casting a function pointer to any integer type" +} + +declare_clippy_lint! { + /// ### What it does /// Checks for casts of `&T` to `&mut T` anywhere in the code. /// /// ### Why is this bad? @@ -360,6 +397,7 @@ impl_lint_pass!(Casts => [ CAST_REF_TO_MUT, CAST_PTR_ALIGNMENT, UNNECESSARY_CAST, + FN_TO_NUMERIC_CAST_ANY, FN_TO_NUMERIC_CAST, FN_TO_NUMERIC_CAST_WITH_TRUNCATION, CHAR_LIT_AS_U8, @@ -385,6 +423,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } + 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) { diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index b7385dcfbca..8abf10c0d1c 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -459,12 +459,10 @@ fn emit_branches_sharing_code_lint( } else { sm.stmt_span(block.stmts[block.stmts.len() - end_stmts].span, block.span) }; - let moved_end = block - .expr - .map_or_else( - || sm.stmt_span(block.stmts[block.stmts.len() - 1].span, block.span), - |expr| expr.span.source_callsite(), - ); + let moved_end = block.expr.map_or_else( + || sm.stmt_span(block.stmts[block.stmts.len() - 1].span, block.span), + |expr| expr.span.source_callsite(), + ); let moved_span = moved_start.to(moved_end); let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None); diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index db8f2171348..cde27d3ad2a 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -1,5 +1,6 @@ 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 if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; @@ -139,6 +140,13 @@ impl LateLintPass<'_> for Default { .fields .iter() .all(|field| field.vis.is_accessible_from(module_did, cx.tcx)); + let all_fields_are_copy = variant + .fields + .iter() + .all(|field| { + is_copy(cx, cx.tcx.type_of(field.did)) + }); + if !has_drop(cx, binding_type) || all_fields_are_copy; then { (local, variant, ident.name, binding_type, expr.span) } else { diff --git a/src/tools/clippy/clippy_lints/src/disallowed_type.rs b/src/tools/clippy/clippy_lints/src/disallowed_type.rs index 87124f093a8..48f781516f4 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_type.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_type.rs @@ -1,12 +1,14 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; -use rustc_data_structures::fx::FxHashSet; +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, Symbol}; +use rustc_span::Span; + +use crate::utils::conf; declare_clippy_lint! { /// ### What it does @@ -19,7 +21,15 @@ declare_clippy_lint! { /// An example clippy.toml configuration: /// ```toml /// # clippy.toml - /// disallowed-types = ["std::collections::BTreeMap"] + /// 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 @@ -38,33 +48,30 @@ declare_clippy_lint! { } #[derive(Clone, Debug)] pub struct DisallowedType { - disallowed: FxHashSet<Vec<Symbol>>, - def_ids: FxHashSet<DefId>, - prim_tys: FxHashSet<PrimTy>, + conf_disallowed: Vec<conf::DisallowedType>, + def_ids: FxHashMap<DefId, Option<String>>, + prim_tys: FxHashMap<PrimTy, Option<String>>, } impl DisallowedType { - pub fn new(disallowed: &FxHashSet<String>) -> Self { + pub fn new(conf_disallowed: Vec<conf::DisallowedType>) -> Self { Self { - disallowed: disallowed - .iter() - .map(|s| s.split("::").map(Symbol::intern).collect::<Vec<_>>()) - .collect(), - def_ids: FxHashSet::default(), - prim_tys: FxHashSet::default(), + 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 self.def_ids.contains(did) { - emit(cx, &cx.tcx.def_path_str(*did), span); + 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 self.prim_tys.contains(prim) { - emit(cx, prim.name_str(), span); + if let Some(reason) = self.prim_tys.get(prim) { + emit(cx, prim.name_str(), span, reason.as_deref()); } }, _ => {}, @@ -76,14 +83,21 @@ impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]); impl<'tcx> LateLintPass<'tcx> for DisallowedType { fn check_crate(&mut self, cx: &LateContext<'_>) { - for path in &self.disallowed { - let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>(); - match clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>()) { + 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); + self.def_ids.insert(id, reason); }, Res::PrimTy(ty) => { - self.prim_tys.insert(ty); + self.prim_tys.insert(ty, reason); }, _ => {}, } @@ -107,11 +121,16 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedType { } } -fn emit(cx: &LateContext<'_>, name: &str, span: Span) { - span_lint( +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); + } + }, ); } diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 9840affbf6f..5511c3ea9b6 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -1,3 +1,4 @@ +use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note}; use clippy_utils::source::first_line_of_span; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; @@ -297,6 +298,17 @@ fn lint_for_missing_headers<'tcx>( if !cx.access_levels.is_exported(def_id) { return; // Private functions do not require doc comments } + + // do not lint if any parent has `#[doc(hidden)]` attribute (#7347) + if cx + .tcx + .hir() + .parent_iter(cx.tcx.hir().local_def_id_to_hir_id(def_id)) + .any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id))) + { + return; + } + if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe { span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/eq_op.rs b/src/tools/clippy/clippy_lints/src/eq_op.rs index 51d5094e8c9..655560afd42 100644 --- a/src/tools/clippy/clippy_lints/src/eq_op.rs +++ b/src/tools/clippy/clippy_lints/src/eq_op.rs @@ -1,7 +1,9 @@ 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}; +use clippy_utils::{ + ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of, is_in_test_function, +}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; @@ -81,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if macro_args.len() == 2; let (lhs, rhs) = (macro_args[0], macro_args[1]); if eq_expr_value(cx, lhs, rhs); - + if !is_in_test_function(cx.tcx, e.hir_id); then { span_lint( cx, @@ -108,7 +110,10 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) { return; } - if is_useless_with_eq_exprs(op.node.into()) && eq_expr_value(cx, left, right) { + if is_useless_with_eq_exprs(op.node.into()) + && eq_expr_value(cx, left, right) + && !is_in_test_function(cx.tcx, e.hir_id) + { span_lint( cx, EQ_OP, diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index 0c6ba91c943..e8b1d6f6eda 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_errors::Applicability; @@ -77,9 +77,9 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { let pat_str = match pat.kind { PatKind::Struct(..) => format!( "({})", - snippet_with_applicability(cx, pat.span, "..", &mut applicability), + snippet_with_context(cx, pat.span, expr.span.ctxt(), "..", &mut applicability).0, ), - _ => snippet_with_applicability(cx, pat.span, "..", &mut applicability).to_string(), + _ => snippet_with_context(cx, pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), }; span_lint_and_sugg( cx, @@ -89,7 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { "try", format!( "{} == {}", - snippet_with_applicability(cx, exp.span, "..", &mut applicability), + snippet_with_context(cx, exp.span, expr.span.ctxt(), "..", &mut applicability).0, pat_str, ), applicability, diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 37d9ea3bdc1..c22f9d0e170 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::FormatExpn; -use clippy_utils::last_path_segment; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -69,8 +68,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { ty::Str => true, _ => false, }; - if format_args.args.iter().all(is_display_arg); - if format_args.fmt_expr.map_or(true, check_unformatted); + if let Some(args) = format_args.args(); + if args.iter().all(|arg| arg.is_display() && !arg.has_string_formatting()); then { let is_new_string = match value.kind { ExprKind::Binary(..) => true, @@ -101,47 +100,3 @@ fn span_useless_format(cx: &LateContext<'_>, span: Span, sugg: String, applicabi applicability, ); } - -fn is_display_arg(expr: &Expr<'_>) -> bool { - if_chain! { - if let ExprKind::Call(_, [_, fmt]) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = fmt.kind; - if let [.., t, _] = path.segments; - if t.ident.name == sym::Display; - then { true } else { false } - } -} - -/// Checks if the expression matches -/// ```rust,ignore -/// &[_ { -/// format: _ { -/// width: _::Implied, -/// precision: _::Implied, -/// ... -/// }, -/// ..., -/// }] -/// ``` -fn check_unformatted(expr: &Expr<'_>) -> bool { - if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; - if let ExprKind::Array([expr]) = expr.kind; - // struct `core::fmt::rt::v1::Argument` - if let ExprKind::Struct(_, fields, _) = expr.kind; - if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format); - // struct `core::fmt::rt::v1::FormatSpec` - if let ExprKind::Struct(_, fields, _) = format_field.expr.kind; - if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym::precision); - if let ExprKind::Path(ref precision_path) = precision_field.expr.kind; - if last_path_segment(precision_path).ident.name == sym::Implied; - if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym::width); - if let ExprKind::Path(ref width_qpath) = width_field.expr.kind; - if last_path_segment(width_qpath).ident.name == sym::Implied; - then { - return true; - } - } - - false -} diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs new file mode 100644 index 00000000000..8b27442aa94 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -0,0 +1,223 @@ +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::higher::{FormatArgsArg, FormatArgsExpn, FormatExpn}; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::implements_trait; +use clippy_utils::{is_diag_trait_item, match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +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}; + +declare_clippy_lint! { + /// ### What it does + /// Detects `format!` within the arguments of another macro that does + /// formatting such as `format!` itself, `write!` or `println!`. Suggests + /// inlining the `format!` call. + /// + /// ### Why is this bad? + /// The recommended code is both shorter and avoids a temporary allocation. + /// + /// ### Example + /// ```rust + /// # use std::panic::Location; + /// println!("error: {}", format!("something failed at {}", Location::caller())); + /// ``` + /// Use instead: + /// ```rust + /// # use std::panic::Location; + /// println!("error: something failed at {}", Location::caller()); + /// ``` + pub FORMAT_IN_FORMAT_ARGS, + perf, + "`format!` used in a macro that does formatting" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for [`ToString::to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string) + /// applied to a type that implements [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html) + /// in a macro that does formatting. + /// + /// ### Why is this bad? + /// Since the type implements `Display`, the use of `to_string` is + /// unnecessary. + /// + /// ### Example + /// ```rust + /// # use std::panic::Location; + /// println!("error: something failed at {}", Location::caller().to_string()); + /// ``` + /// Use instead: + /// ```rust + /// # use std::panic::Location; + /// println!("error: something failed at {}", Location::caller()); + /// ``` + pub TO_STRING_IN_FORMAT_ARGS, + perf, + "`to_string` applied to a type that implements `Display` in format args" +} + +declare_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); + +const FORMAT_MACRO_PATHS: &[&[&str]] = &[ + &paths::FORMAT_ARGS_MACRO, + &paths::ASSERT_EQ_MACRO, + &paths::ASSERT_MACRO, + &paths::ASSERT_NE_MACRO, + &paths::EPRINT_MACRO, + &paths::EPRINTLN_MACRO, + &paths::PRINT_MACRO, + &paths::PRINTLN_MACRO, + &paths::WRITE_MACRO, + &paths::WRITELN_MACRO, +]; + +const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[sym::format_macro, sym::std_panic_macro]; + +impl<'tcx> LateLintPass<'tcx> for FormatArgs { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if_chain! { + if let Some(format_args) = FormatArgsExpn::parse(expr); + let expr_expn_data = expr.span.ctxt().outer_expn_data(); + let outermost_expn_data = outermost_expn_data(expr_expn_data); + if let Some(macro_def_id) = outermost_expn_data.macro_def_id; + if FORMAT_MACRO_PATHS + .iter() + .any(|path| match_def_path(cx, macro_def_id, path)) + || FORMAT_MACRO_DIAG_ITEMS + .iter() + .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, macro_def_id)); + if let ExpnKind::Macro(_, name) = outermost_expn_data.kind; + if let Some(args) = format_args.args(); + then { + for (i, arg) in args.iter().enumerate() { + if !arg.is_display() { + continue; + } + if arg.has_string_formatting() { + continue; + } + if is_aliased(&args, i) { + continue; + } + check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg); + check_to_string_in_format_args(cx, name, arg); + } + } + } + } +} + +fn outermost_expn_data(expn_data: ExpnData) -> ExpnData { + if expn_data.call_site.from_expansion() { + outermost_expn_data(expn_data.call_site.ctxt().outer_expn_data()) + } else { + expn_data + } +} + +fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &FormatArgsArg<'_>) { + if_chain! { + if FormatExpn::parse(arg.value).is_some(); + if !arg.value.span.ctxt().outer_expn_data().call_site.from_expansion(); + then { + span_lint_and_then( + cx, + FORMAT_IN_FORMAT_ARGS, + trim_semicolon(cx, call_site), + &format!("`format!` in `{}!` args", name), + |diag| { + diag.help(&format!( + "combine the `format!(..)` arguments with the outer `{}!(..)` call", + name + )); + diag.help("or consider changing `format!` to `format_args!`"); + }, + ); + } + } +} + +fn check_to_string_in_format_args<'tcx>(cx: &LateContext<'tcx>, name: Symbol, arg: &FormatArgsArg<'tcx>) { + let value = arg.value; + if_chain! { + if !value.span.from_expansion(); + if let ExprKind::MethodCall(_, _, [receiver], _) = value.kind; + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id); + if is_diag_trait_item(cx, method_def_id, sym::ToString); + let receiver_ty = cx.typeck_results().expr_ty(receiver); + if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display); + if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); + then { + let (n_needed_derefs, target) = count_needed_derefs( + receiver_ty, + cx.typeck_results().expr_adjustments(receiver).iter(), + ); + if implements_trait(cx, target, display_trait_id, &[]) { + if n_needed_derefs == 0 { + span_lint_and_sugg( + cx, + TO_STRING_IN_FORMAT_ARGS, + value.span.with_lo(receiver.span.hi()), + &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name), + "remove this", + String::new(), + Applicability::MachineApplicable, + ); + } else { + span_lint_and_sugg( + cx, + TO_STRING_IN_FORMAT_ARGS, + value.span, + &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name), + "use this", + format!("{:*>width$}{}", "", receiver_snippet, width = n_needed_derefs), + Applicability::MachineApplicable, + ); + } + } + } + } +} + +// Returns true if `args[i]` "refers to" or "is referred to by" another argument. +fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool { + let value = args[i].value; + args.iter() + .enumerate() + .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>>, +{ + let mut n_total = 0; + let mut n_needed = 0; + loop { + if let Some(Adjustment { + kind: Adjust::Deref(overloaded_deref), + target, + }) = iter.next() + { + n_total += 1; + if overloaded_deref.is_some() { + n_needed = n_total; + } + ty = target; + } else { + return (n_needed, ty); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index 04fc5887e8e..d7c5ec9ba35 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -91,11 +91,9 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for a [`#[must_use]`] attribute on + /// Checks for a `#[must_use]` attribute on /// unit-returning functions and methods. /// - /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// /// ### Why is this bad? /// Unit values are useless. The attribute is likely /// a remnant of a refactoring that removed the return type. @@ -112,12 +110,10 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for a [`#[must_use]`] attribute without + /// Checks for a `#[must_use]` attribute without /// further information on functions and methods that return a type already /// marked as `#[must_use]`. /// - /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// /// ### Why is this bad? /// The attribute isn't needed. Not using the result /// will already be reported. Alternatively, one can add some text to the @@ -138,11 +134,9 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for public functions that have no - /// [`#[must_use]`] attribute, but return something not already marked + /// `#[must_use]` attribute, but return something not already marked /// must-use, have no mutable arg and mutate no statics. /// - /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// /// ### Why is this bad? /// Not bad at all, this lint just shows places where /// you could add the attribute. diff --git a/src/tools/clippy/clippy_lints/src/identity_op.rs b/src/tools/clippy/clippy_lints/src/identity_op.rs index 73bdd67ff5d..414f465c494 100644 --- a/src/tools/clippy/clippy_lints/src/identity_op.rs +++ b/src/tools/clippy/clippy_lints/src/identity_op.rs @@ -66,7 +66,6 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_ && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)) } -#[allow(clippy::cast_possible_wrap)] fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e) { let check = match *cx.typeck_results().expr_ty(e).kind() { diff --git a/src/tools/clippy/clippy_lints/src/if_then_panic.rs b/src/tools/clippy/clippy_lints/src/if_then_panic.rs index 10bca59e6d0..e8cea5529e8 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_panic.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_panic.rs @@ -78,10 +78,10 @@ impl LateLintPass<'_> for IfThenPanic { if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e { sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string() } else { - format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par().to_string()) + format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par()) } } else { - format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par().to_string()) + format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par()) }; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index 79d4d7ddcbc..a4f60ded3a6 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { return; } if_chain! { - if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); + if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr); // Check if the conditional expression is a binary operation if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind; diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs index 3c40ca50a09..61dd0eb4af7 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs @@ -138,10 +138,10 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { item.span, &format!( "type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`", - self_type.to_string() + self_type ), None, - &format!("remove the inherent method from type `{}`", self_type.to_string()), + &format!("remove the inherent method from type `{}`", self_type), ); } else { span_lint_and_help( @@ -150,10 +150,10 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { item.span, &format!( "implementation of inherent method `to_string(&self) -> String` for type `{}`", - self_type.to_string() + self_type ), None, - &format!("implement trait `Display` for type `{}` instead", self_type.to_string()), + &format!("implement trait `Display` for type `{}` instead", self_type), ); } } diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 89146b4dd2c..9efd7aba7e8 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -10,12 +10,11 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// Checks for `let _ = <expr>` - /// where expr is #[must_use] + /// Checks for `let _ = <expr>` where expr is `#[must_use]` /// /// ### Why is this bad? - /// It's better to explicitly - /// handle the value of a #[must_use] expr + /// It's better to explicitly handle the value of a `#[must_use]` + /// expr /// /// ### Example /// ```rust diff --git a/src/tools/clippy/clippy_lints/src/lib.register_all.rs b/src/tools/clippy/clippy_lints/src/lib.register_all.rs index 6a3ee35b41a..c949ee23ecc 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_all.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_all.rs @@ -60,6 +60,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(float_literal::EXCESSIVE_PRECISION), LintId::of(format::USELESS_FORMAT), + LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), + LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), LintId::of(formatting::POSSIBLE_MISSING_COMMA), LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), @@ -118,6 +120,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(match_result_ok::MATCH_RESULT_OK), + LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), @@ -265,6 +268,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), + LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), @@ -277,6 +281,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(types::VEC_BOX), LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(unicode::INVISIBLE_CHARACTERS), + LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(unit_types::UNIT_ARG), LintId::of(unit_types::UNIT_CMP), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs index 64b82fc0faa..c51341bdf0c 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs @@ -82,6 +82,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), + LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(types::BORROWED_BOX), LintId::of(types::TYPE_COMPLEXITY), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs b/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs index bbe47a0e772..ff56a6081fb 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs @@ -36,6 +36,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::NEVER_LOOP), LintId::of(loops::WHILE_IMMUTABLE_CONDITION), + LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH), LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::ITERATOR_STEP_BY_ZERO), @@ -62,6 +63,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(transmuting_null::TRANSMUTING_NULL), LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), LintId::of(unicode::INVISIBLE_CHARACTERS), + LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(unit_types::UNIT_CMP), LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs index b0be3b65366..e8dd3708c8e 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs @@ -67,6 +67,7 @@ store.register_lints(&[ casts::CAST_SIGN_LOSS, casts::CHAR_LIT_AS_U8, casts::FN_TO_NUMERIC_CAST, + casts::FN_TO_NUMERIC_CAST_ANY, casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, casts::PTR_AS_PTR, casts::UNNECESSARY_CAST, @@ -138,6 +139,8 @@ store.register_lints(&[ floating_point_arithmetic::IMPRECISE_FLOPS, floating_point_arithmetic::SUBOPTIMAL_FLOPS, format::USELESS_FORMAT, + format_args::FORMAT_IN_FORMAT_ARGS, + format_args::TO_STRING_IN_FORMAT_ARGS, formatting::POSSIBLE_MISSING_COMMA, formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, formatting::SUSPICIOUS_ELSE_FORMATTING, @@ -225,6 +228,7 @@ store.register_lints(&[ map_unit_fn::RESULT_MAP_UNIT_FN, match_on_vec_items::MATCH_ON_VEC_ITEMS, match_result_ok::MATCH_RESULT_OK, + match_str_case_mismatch::MATCH_STR_CASE_MISMATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, matches::MATCH_AS_REF, matches::MATCH_BOOL, @@ -359,6 +363,7 @@ store.register_lints(&[ neg_multiply::NEG_MULTIPLY, new_without_default::NEW_WITHOUT_DEFAULT, no_effect::NO_EFFECT, + no_effect::NO_EFFECT_UNDERSCORE_BINDING, no_effect::UNNECESSARY_OPERATION, non_copy_const::BORROW_INTERIOR_MUTABLE_CONST, non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST, @@ -438,6 +443,7 @@ store.register_lints(&[ temporary_assignment::TEMPORARY_ASSIGNMENT, to_digit_is_some::TO_DIGIT_IS_SOME, to_string_in_display::TO_STRING_IN_DISPLAY, + trailing_empty_array::TRAILING_EMPTY_ARRAY, trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, trait_bounds::TYPE_REPETITION_IN_BOUNDS, transmute::CROSSPOINTER_TRANSMUTE, @@ -447,6 +453,7 @@ store.register_lints(&[ transmute::TRANSMUTE_INT_TO_BOOL, transmute::TRANSMUTE_INT_TO_CHAR, transmute::TRANSMUTE_INT_TO_FLOAT, + transmute::TRANSMUTE_NUM_TO_BYTES, transmute::TRANSMUTE_PTR_TO_PTR, transmute::TRANSMUTE_PTR_TO_REF, transmute::UNSOUND_COLLECTION_TRANSMUTE, @@ -463,10 +470,12 @@ store.register_lints(&[ types::REDUNDANT_ALLOCATION, types::TYPE_COMPLEXITY, types::VEC_BOX, + undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS, undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, unicode::INVISIBLE_CHARACTERS, unicode::NON_ASCII_LITERAL, unicode::UNICODE_NOT_NFC, + uninit_vec::UNINIT_VEC, unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, unit_types::LET_UNIT_VALUE, unit_types::UNIT_ARG, diff --git a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs index 96e0b421094..1e54482a8da 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs @@ -25,6 +25,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(regex::TRIVIAL_REGEX), LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), + LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), ]) diff --git a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs index 6533b94e82b..268349d2848 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs @@ -72,6 +72,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ 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(no_effect::NO_EFFECT_UNDERSCORE_BINDING), LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(non_expressive_names::SIMILAR_NAMES), LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs index 5432345760b..a0d5cf9418e 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs @@ -5,6 +5,8 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(entry::MAP_ENTRY), LintId::of(escape::BOXED_LOCAL), + LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), + LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(loops::MANUAL_MEMCPY), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs index 4463dea5fcb..3d68a6e9009 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs @@ -8,6 +8,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(as_conversions::AS_CONVERSIONS), LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), + LintId::of(casts::FN_TO_NUMERIC_CAST_ANY), LintId::of(create_dir::CREATE_DIR), LintId::of(dbg_macro::DBG_MACRO), LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), @@ -56,6 +57,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(strings::STR_TO_STRING), LintId::of(types::RC_BUFFER), LintId::of(types::RC_MUTEX), + LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS), LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(verbose_file_reads::VERBOSE_FILE_READS), diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 5534f9c94f3..ed7e8277023 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -218,6 +218,7 @@ mod float_equality_without_abs; mod float_literal; mod floating_point_arithmetic; mod format; +mod format_args; mod formatting; mod from_over_into; mod from_str_radix_10; @@ -265,6 +266,7 @@ mod map_err_ignore; mod map_unit_fn; mod match_on_vec_items; mod match_result_ok; +mod match_str_case_mismatch; mod matches; mod mem_forget; mod mem_replace; @@ -352,13 +354,16 @@ mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; mod to_string_in_display; +mod trailing_empty_array; mod trait_bounds; mod transmute; mod transmuting_null; mod try_err; mod types; +mod undocumented_unsafe_blocks; mod undropped_manually_drops; mod unicode; +mod uninit_vec; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; @@ -516,6 +521,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions)); store.register_late_pass(|| Box::new(collapsible_match::CollapsibleMatch)); store.register_late_pass(|| Box::new(unicode::Unicode)); + store.register_late_pass(|| Box::new(uninit_vec::UninitVec)); store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)); store.register_late_pass(|| Box::new(strings::StringAdd)); store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn)); @@ -754,8 +760,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison)); 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.iter().cloned().collect::<FxHashSet<_>>(); - store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(&disallowed_types))); + let disallowed_types = conf.disallowed_types.clone(); + store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::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()))); let scripts = conf.allowed_scripts.clone(); @@ -767,6 +773,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic)); 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(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)); + } #[rustfmt::skip] diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs index 3db1f0421ea..ecf6ad316a4 100644 --- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs +++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// } /// /// if let Ok(value) = iter.next() { - /// vec.push_value) + /// vec.push(value) /// } /// ``` pub MATCH_RESULT_OK, diff --git a/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs new file mode 100644 index 00000000000..a83f38e3d51 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs @@ -0,0 +1,171 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::SymbolStr; +use rustc_span::{sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `match` expressions modifying the case of a string with non-compliant arms + /// + /// ### Why is this bad? + /// The arm is unreachable, which is likely a mistake + /// + /// ### Example + /// ```rust + /// # let text = "Foo"; + /// + /// match &*text.to_ascii_lowercase() { + /// "foo" => {}, + /// "Bar" => {}, + /// _ => {}, + /// } + /// ``` + /// Use instead: + /// ```rust + /// # let text = "Foo"; + /// + /// match &*text.to_ascii_lowercase() { + /// "foo" => {}, + /// "bar" => {}, + /// _ => {}, + /// } + /// ``` + pub MATCH_STR_CASE_MISMATCH, + correctness, + "creation of a case altering match expression with non-compliant arms" +} + +declare_lint_pass!(MatchStrCaseMismatch => [MATCH_STR_CASE_MISMATCH]); + +#[derive(Debug)] +enum CaseMethod { + LowerCase, + AsciiLowerCase, + UpperCase, + AsciiUppercase, +} + +impl LateLintPass<'_> for MatchStrCaseMismatch { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if !in_external_macro(cx.tcx.sess, expr.span); + if let ExprKind::Match(match_expr, arms, MatchSource::Normal) = expr.kind; + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(match_expr).kind(); + if let ty::Str = ty.kind(); + then { + let mut visitor = MatchExprVisitor { + cx, + case_method: None, + }; + + visitor.visit_expr(match_expr); + + if let Some(case_method) = visitor.case_method { + if let Some((bad_case_span, bad_case_str)) = verify_case(&case_method, arms) { + lint(cx, &case_method, bad_case_span, &bad_case_str); + } + } + } + } + } +} + +struct MatchExprVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + case_method: Option<CaseMethod>, +} + +impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { + match ex.kind { + ExprKind::MethodCall(segment, _, [receiver], _) + if self.case_altered(&*segment.ident.as_str(), receiver) => {}, + _ => walk_expr(self, ex), + } + } +} + +impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> { + fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool { + if let Some(case_method) = get_case_method(segment_ident) { + let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); + + if is_type_diagnostic_item(self.cx, ty, sym::String) || ty.kind() == &ty::Str { + self.case_method = Some(case_method); + return true; + } + } + + false + } +} + +fn get_case_method(segment_ident_str: &str) -> Option<CaseMethod> { + match segment_ident_str { + "to_lowercase" => Some(CaseMethod::LowerCase), + "to_ascii_lowercase" => Some(CaseMethod::AsciiLowerCase), + "to_uppercase" => Some(CaseMethod::UpperCase), + "to_ascii_uppercase" => Some(CaseMethod::AsciiUppercase), + _ => None, + } +} + +fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(Span, SymbolStr)> { + let case_check = match case_method { + CaseMethod::LowerCase => |input: &str| -> bool { input.chars().all(char::is_lowercase) }, + CaseMethod::AsciiLowerCase => |input: &str| -> bool { input.chars().all(|c| matches!(c, 'a'..='z')) }, + CaseMethod::UpperCase => |input: &str| -> bool { input.chars().all(char::is_uppercase) }, + CaseMethod::AsciiUppercase => |input: &str| -> bool { input.chars().all(|c| matches!(c, 'A'..='Z')) }, + }; + + for arm in arms { + if_chain! { + if let PatKind::Lit(Expr { + kind: ExprKind::Lit(lit), + .. + }) = arm.pat.kind; + if let LitKind::Str(symbol, _) = lit.node; + let input = symbol.as_str(); + if !case_check(&input); + then { + return Some((lit.span, input)); + } + } + } + + None +} + +fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad_case_str: &str) { + let (method_str, suggestion) = match case_method { + CaseMethod::LowerCase => ("to_lower_case", bad_case_str.to_lowercase()), + CaseMethod::AsciiLowerCase => ("to_ascii_lowercase", bad_case_str.to_ascii_lowercase()), + CaseMethod::UpperCase => ("to_uppercase", bad_case_str.to_uppercase()), + CaseMethod::AsciiUppercase => ("to_ascii_uppercase", bad_case_str.to_ascii_uppercase()), + }; + + span_lint_and_sugg( + cx, + MATCH_STR_CASE_MISMATCH, + bad_case_span, + "this `match` arm has a differing case than its expression", + &*format!("consider changing the case of this arm to respect `{}`", method_str), + format!("\"{}\"", suggestion), + Applicability::MachineApplicable, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs index 56d4163a6b3..b643fba5d32 100644 --- a/src/tools/clippy/clippy_lints/src/matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -1187,7 +1187,7 @@ where 'b: 'a, I: Clone + Iterator<Item = &'a Pat<'b>>, { - if !has_only_ref_pats(pats.clone()) { + if !has_multiple_ref_pats(pats.clone()) { return; } @@ -1693,12 +1693,12 @@ fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotat None } -fn has_only_ref_pats<'a, 'b, I>(pats: I) -> bool +fn has_multiple_ref_pats<'a, 'b, I>(pats: I) -> bool where 'b: 'a, I: Iterator<Item = &'a Pat<'b>>, { - let mut at_least_one_is_true = false; + let mut ref_count = 0; for opt in pats.map(|pat| match pat.kind { PatKind::Ref(..) => Some(true), // &-patterns PatKind::Wild => Some(false), // an "anything" wildcard is also fine @@ -1706,13 +1706,13 @@ where }) { if let Some(inner) = opt { if inner { - at_least_one_is_true = true; + ref_count += 1; } } else { return false; } } - at_least_one_is_true + ref_count > 1 } pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)> diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index b26d11c0d6b..26c29fbb289 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -1777,14 +1777,13 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usages of `str::splitn(2, _)` - /// - /// **Why is this bad?** `split_once` is both clearer in intent and slightly more efficient. - /// - /// **Known problems:** None. + /// ### What it does + /// Checks for usages of `str::splitn(2, _)` /// - /// **Example:** + /// ### Why is this bad? + /// `split_once` is both clearer in intent and slightly more efficient. /// + /// ### Example /// ```rust,ignore /// // Bad /// let (key, value) = _.splitn(2, '=').next_tuple()?; diff --git a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs index 1a5894e48d1..ce89189bce9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_expr_path_def_path, match_def_path, paths}; +use clippy_utils::{is_expr_path_def_path, paths, ty::is_uninit_value_valid_for_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; use super::UNINIT_ASSUMED_INIT; @@ -13,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if let hir::ExprKind::Call(callee, args) = recv.kind; if args.is_empty(); if is_expr_path_def_path(cx, callee, &paths::MEM_MAYBEUNINIT_UNINIT); - if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr)); + if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr)); then { span_lint( cx, @@ -24,12 +23,3 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr } } } - -fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Array(component, _) => is_maybe_uninit_ty_valid(cx, component), - ty::Tuple(types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)), - ty::Adt(adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), - _ => false, - } -} diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 667cdd83025..b593c747498 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -8,7 +8,7 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// it lints if an exported function, method, trait method with default impl, + /// It lints if an exported function, method, trait method with default impl, /// or trait method impl is not `#[inline]`. /// /// ### Why is this bad? diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs index 610152a217f..7c4cac29ba8 100644 --- a/src/tools/clippy/clippy_lints/src/mut_mut.rs +++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs @@ -82,6 +82,10 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { } fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { + if in_external_macro(self.cx.sess(), ty.span) { + return; + } + if let hir::TyKind::Rptr( _, hir::MutTy { diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index c5a5cde4b11..6dae8f32043 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::is_lint_allowed; use clippy_utils::source::snippet_opt; use clippy_utils::ty::has_drop; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource}; +use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::ops::Deref; @@ -13,7 +14,7 @@ declare_clippy_lint! { /// Checks for statements which have no effect. /// /// ### Why is this bad? - /// Similar to dead code, these statements are actually + /// Unlike dead code, these statements are actually /// executed. However, as they have no effect, all they do is make the code less /// readable. /// @@ -28,6 +29,28 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does + /// Checks for binding to underscore prefixed variable without side-effects. + /// + /// ### Why is this bad? + /// Unlike dead code, these bindings are actually + /// executed. However, as they have no effect and shouldn't be used further on, all they + /// do is make the code less readable. + /// + /// ### Known problems + /// Further usage of this variable is not checked, which can lead to false positives if it is + /// used later in the code. + /// + /// ### Example + /// ```rust,ignore + /// let _i_serve_no_purpose = 1; + /// ``` + pub NO_EFFECT_UNDERSCORE_BINDING, + pedantic, + "binding to `_` prefixed variable with no side-effect" +} + +declare_clippy_lint! { + /// ### What it does /// Checks for expression statements that can be reduced to a /// sub-expression. /// @@ -44,6 +67,46 @@ declare_clippy_lint! { "outer expressions with no effect" } +declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]); + +impl<'tcx> LateLintPass<'tcx> for NoEffect { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if check_no_effect(cx, stmt) { + return; + } + check_unnecessary_operation(cx, stmt); + } +} + +fn check_no_effect(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> bool { + if let StmtKind::Semi(expr) = stmt.kind { + if has_no_effect(cx, expr) { + span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); + return true; + } + } else if let StmtKind::Local(local) = stmt.kind { + if_chain! { + if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id); + if let Some(init) = local.init; + if !local.pat.span.from_expansion(); + if has_no_effect(cx, init); + if let PatKind::Binding(_, _, ident, _) = local.pat.kind; + if ident.name.to_ident_string().starts_with('_'); + then { + span_lint_hir( + cx, + NO_EFFECT_UNDERSCORE_BINDING, + init.hir_id, + stmt.span, + "binding to `_` prefixed variable with no side-effect" + ); + return true; + } + } + } + false +} + fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if expr.span.from_expansion() { return false; @@ -88,71 +151,59 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } -declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION]); - -impl<'tcx> LateLintPass<'tcx> for NoEffect { - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Semi(expr) = stmt.kind { - if has_no_effect(cx, expr) { - span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); - } else if let Some(reduced) = reduce_expression(cx, expr) { - for e in &reduced { - if e.span.from_expansion() { - return; - } - } - if let ExprKind::Index(..) = &expr.kind { - let snippet; - if_chain! { - if let Some(arr) = snippet_opt(cx, reduced[0].span); - if let Some(func) = snippet_opt(cx, reduced[1].span); - then { - snippet = format!("assert!({}.len() > {});", &arr, &func); - } else { - return; - } - } - span_lint_hir_and_then( - cx, - UNNECESSARY_OPERATION, - expr.hir_id, - stmt.span, - "unnecessary operation", - |diag| { - diag.span_suggestion( - stmt.span, - "statement can be written as", - snippet, - Applicability::MaybeIncorrect, - ); - }, - ); +fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if_chain! { + if let StmtKind::Semi(expr) = stmt.kind; + if let Some(reduced) = reduce_expression(cx, 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); } else { - let mut snippet = String::new(); - for e in reduced { - if let Some(snip) = snippet_opt(cx, e.span) { - snippet.push_str(&snip); - snippet.push(';'); - } else { - return; - } + return; + } + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be written as", + snippet, + Applicability::MaybeIncorrect, + ); + }, + ); + } else { + let mut snippet = String::new(); + for e in reduced { + if let Some(snip) = snippet_opt(cx, e.span) { + snippet.push_str(&snip); + snippet.push(';'); + } else { + return; } - span_lint_hir_and_then( - cx, - UNNECESSARY_OPERATION, - expr.hir_id, - stmt.span, - "unnecessary operation", - |diag| { - diag.span_suggestion( - stmt.span, - "statement can be reduced to", - snippet, - Applicability::MachineApplicable, - ); - }, - ); } + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be reduced to", + snippet, + Applicability::MachineApplicable, + ); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index d180d6f9227..92a4801a846 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -139,6 +139,7 @@ declare_clippy_lint! { /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); } /// ``` /// + /// ```ignore /// // Good /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); } /// ``` diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index aa6d254e7a5..4d616e26bfc 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -4,10 +4,10 @@ use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, path_to_local_id}; +use clippy_utils::{eq_expr_value, path_to_local, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultOk}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -48,16 +48,20 @@ impl QuestionMark { /// } /// ``` /// + /// ```ignore + /// if result.is_err() { + /// return result; + /// } + /// ``` + /// /// If it matches, it will suggest to use the question mark operator instead - fn check_is_none_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) { + fn check_is_none_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr); if let ExprKind::MethodCall(segment, _, args, _) = &cond.kind; - if segment.ident.name == sym!(is_none); - if Self::expression_returns_none(cx, then); if let Some(subject) = args.get(0); - if Self::is_option(cx, subject); - + if (Self::option_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_none)) || + (Self::result_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_err)); then { let mut applicability = Applicability::MachineApplicable; let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability); @@ -95,31 +99,24 @@ impl QuestionMark { } } - fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) { + fn check_if_let_some_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr); - if Self::is_option(cx, let_expr); - if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind; - if is_lang_ctor(cx, path1, OptionSome); + if (Self::option_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, OptionSome)) || + (Self::result_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, ResultOk)); + if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind; let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); - if let ExprKind::Block(block, None) = if_then.kind; if block.stmts.is_empty(); if let Some(trailing_expr) = &block.expr; if path_to_local_id(trailing_expr, bind_id); - - if Self::expression_returns_none(cx, if_else); then { let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); - let replacement = format!( - "{}{}?", - receiver_str, - if by_ref { ".as_ref()" } else { "" }, - ); + let replacement = format!("{}{}?", receiver_str, if by_ref { ".as_ref()" } else { "" },); span_lint_and_sugg( cx, @@ -134,6 +131,14 @@ impl QuestionMark { } } + fn result_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool { + Self::is_result(cx, expr) && Self::expression_returns_unmodified_err(cx, nested_expr, expr) + } + + fn option_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool { + Self::is_option(cx, expr) && Self::expression_returns_none(cx, nested_expr) + } + fn moves_by_default(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expression); @@ -146,6 +151,12 @@ impl QuestionMark { is_type_diagnostic_item(cx, expr_ty, sym::Option) } + fn is_result(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expression); + + is_type_diagnostic_item(cx, expr_ty, sym::Result) + } + fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { match expression.kind { ExprKind::Block(block, _) => { @@ -161,6 +172,27 @@ impl QuestionMark { } } + fn expression_returns_unmodified_err( + cx: &LateContext<'_>, + expression: &Expr<'_>, + origin_hir_id: &Expr<'_>, + ) -> bool { + match expression.kind { + ExprKind::Block(block, _) => { + if let Some(return_expression) = Self::return_expression(block) { + return Self::expression_returns_unmodified_err(cx, return_expression, origin_hir_id); + } + + false + }, + ExprKind::Ret(Some(expr)) | ExprKind::Call(expr, _) => { + Self::expression_returns_unmodified_err(cx, expr, origin_hir_id) + }, + ExprKind::Path(_) => path_to_local(expression) == path_to_local(origin_hir_id), + _ => false, + } + } + fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { // Check if last expression is a return statement. Then, return the expression if_chain! { @@ -189,7 +221,7 @@ impl QuestionMark { impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - Self::check_is_none_and_early_return_none(cx, expr); - Self::check_if_let_some_and_early_return_none(cx, expr); + Self::check_is_none_or_err_and_early_return(cx, expr); + Self::check_if_let_some_or_err_and_early_return(cx, expr); } } diff --git a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs index 6966230156c..c0e4914efe0 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,7 +1,7 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{in_macro, sugg}; +use clippy_utils::sugg; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, ExprKind}; @@ -39,7 +39,7 @@ declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED] impl LateLintPass<'_> for SemicolonIfNothingReturned { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if_chain! { - if !in_macro(block.span); + if !block.span.from_expansion(); if let Some(expr) = block.expr; let t_expr = cx.typeck_results().expr_ty(expr); if t_expr.is_unit(); diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 2ca7c18800e..64841f33cc3 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -162,11 +162,7 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) (SHADOW_SAME, msg) }, Some(expr) if is_local_used(cx, expr, shadowed) => { - let msg = format!( - "`{}` is shadowed by `{}` which reuses the original value", - snippet(cx, pat.span, "_"), - snippet(cx, expr.span, "..") - ); + let msg = format!("`{}` is shadowed", snippet(cx, pat.span, "_")); (SHADOW_REUSE, msg) }, _ => { diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index 44d5ff0b63a..201aa067824 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -678,7 +678,7 @@ fn suggestion_with_swapped_ident( Some(format!( "{}{}{}", snippet_with_applicability(cx, expr.span.with_hi(current_ident.span.lo()), "..", applicability), - new_ident.to_string(), + new_ident, snippet_with_applicability(cx, expr.span.with_lo(current_ident.span.hi()), "..", applicability), )) }) diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs new file mode 100644 index 00000000000..c216a1f81ea --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::{HirId, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Const; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Displays a warning when a struct with a trailing zero-sized array is declared without a `repr` attribute. + /// + /// ### Why is this bad? + /// Zero-sized arrays aren't very useful in Rust itself, so such a struct is likely being created to pass to C code or in some other situation where control over memory layout matters (for example, in conjuction with manual allocation to make it easy to compute the offset of the array). Either way, `#[repr(C)]` (or another `repr` attribute) is needed. + /// + /// ### Example + /// ```rust + /// struct RarelyUseful { + /// some_field: u32, + /// last: [u32; 0], + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// #[repr(C)] + /// struct MoreOftenUseful { + /// some_field: usize, + /// last: [u32; 0], + /// } + /// ``` + pub TRAILING_EMPTY_ARRAY, + nursery, + "struct with a trailing zero-sized array but without `#[repr(C)]` or another `repr` attribute" +} +declare_lint_pass!(TrailingEmptyArray => [TRAILING_EMPTY_ARRAY]); + +impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) { + span_lint_and_help( + cx, + TRAILING_EMPTY_ARRAY, + item.span, + "trailing zero-sized array in a struct which is not marked with a `repr` attribute", + None, + &format!( + "consider annotating `{}` with `#[repr(C)]` or another `repr` attribute", + cx.tcx.def_path_str(item.def_id.to_def_id()) + ), + ); + } + } +} + +fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { + if_chain! { + // First check if last field is an array + if let ItemKind::Struct(data, _) = &item.kind; + if let Some(last_field) = data.fields().last(); + if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind; + + // Then check if that that array zero-sized + let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); + let length = Const::from_anon_const(cx.tcx, length_ldid); + let length = length.try_eval_usize(cx.tcx, cx.param_env); + if let Some(length) = length; + then { + length == 0 + } else { + false + } + } +} + +fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { + cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::repr)) +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index 33ec9c331ce..e6acf1a94c9 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -3,6 +3,7 @@ mod transmute_float_to_int; mod transmute_int_to_bool; mod transmute_int_to_char; mod transmute_int_to_float; +mod transmute_num_to_bytes; mod transmute_ptr_to_ptr; mod transmute_ptr_to_ref; mod transmute_ref_to_ref; @@ -263,6 +264,28 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does + /// Checks for transmutes from a number to an array of `u8` + /// + /// ### Why this is bad? + /// Transmutes are dangerous and error-prone, whereas `to_ne_bytes` + /// is intuitive and safe. + /// + /// ### Example + /// ```rust + /// unsafe { + /// let x: [u8; 8] = std::mem::transmute(1i64); + /// } + /// + /// // should be + /// let x: [u8; 8] = 0i64.to_ne_bytes(); + /// ``` + pub TRANSMUTE_NUM_TO_BYTES, + complexity, + "transmutes from a number to an array of `u8`" +} + +declare_clippy_lint! { + /// ### What it does /// Checks for transmutes from a pointer to a pointer, or /// from a reference to a reference. /// @@ -330,6 +353,7 @@ declare_lint_pass!(Transmute => [ TRANSMUTE_INT_TO_BOOL, TRANSMUTE_INT_TO_FLOAT, TRANSMUTE_FLOAT_TO_INT, + TRANSMUTE_NUM_TO_BYTES, UNSOUND_COLLECTION_TRANSMUTE, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, ]); @@ -365,6 +389,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { linted |= transmute_int_to_bool::check(cx, e, from_ty, to_ty, args); linted |= transmute_int_to_float::check(cx, e, from_ty, to_ty, args, const_context); linted |= transmute_float_to_int::check(cx, e, from_ty, to_ty, args, const_context); + linted |= transmute_num_to_bytes::check(cx, e, from_ty, to_ty, args, const_context); linted |= unsound_collection_transmute::check(cx, e, from_ty, to_ty); if !linted { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs index 8f884e6a4a1..e83d2e06b9a 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using", - format!("std::char::from_u32({}).unwrap()", arg.to_string()), + format!("std::char::from_u32({}).unwrap()", arg), Applicability::Unspecified, ); }, diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs index 2b6a4cff81e..05eee380d6f 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using", - format!("{}::from_bits({})", to_ty, arg.to_string()), + format!("{}::from_bits({})", to_ty, arg), Applicability::Unspecified, ); }, diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs new file mode 100644 index 00000000000..5ba58a76494 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs @@ -0,0 +1,49 @@ +use super::TRANSMUTE_NUM_TO_BYTES; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty, UintTy}; + +/// Checks for `transmute_int_to_float` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + args: &'tcx [Expr<'_>], + const_context: bool, +) -> bool { + match (&from_ty.kind(), &to_ty.kind()) { + (ty::Int(_) | ty::Uint(_) | ty::Float(_), ty::Array(arr_ty, _)) => { + if !matches!(arr_ty.kind(), ty::Uint(UintTy::U8)) { + return false; + } + if matches!(from_ty.kind(), ty::Float(_)) && const_context { + // TODO: Remove when const_float_bits_conv is stabilized + // rust#72447 + return false; + } + + span_lint_and_then( + cx, + TRANSMUTE_NUM_TO_BYTES, + e.span, + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + |diag| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + diag.span_suggestion( + e.span, + "consider using `to_ne_bytes()`", + format!("{}.to_ne_bytes()", arg), + Applicability::Unspecified, + ); + }, + ); + true + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs new file mode 100644 index 00000000000..e08e4d03c7e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -0,0 +1,225 @@ +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +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}; +use rustc_lexer::TokenKind; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::TyCtxt; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{BytePos, Span}; +use std::borrow::Cow; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `unsafe` blocks without a `// Safety: ` comment + /// explaining why the unsafe operations performed inside + /// the block are safe. + /// + /// ### Why is this bad? + /// Undocumented unsafe blocks can make it difficult to + /// read and maintain code, as well as uncover unsoundness + /// and bugs. + /// + /// ### Example + /// ```rust + /// use std::ptr::NonNull; + /// let a = &mut 42; + /// + /// let ptr = unsafe { NonNull::new_unchecked(a) }; + /// ``` + /// Use instead: + /// ```rust + /// use std::ptr::NonNull; + /// let a = &mut 42; + /// + /// // Safety: references are guaranteed to be non-null. + /// let ptr = unsafe { NonNull::new_unchecked(a) }; + /// ``` + pub UNDOCUMENTED_UNSAFE_BLOCKS, + restriction, + "creating an unsafe block without explaining why it is safe" +} + +impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]); + +#[derive(Default)] +pub struct UndocumentedUnsafeBlocks { + pub local_level: u32, + pub local_span: Option<Span>, + // The local was already checked for an overall safety comment + // There is no need to continue checking the blocks in the local + pub local_checked: bool, + // Since we can only check the blocks from expanded macros + // We have to omit the suggestion due to the actual definition + // Not being available to us + pub macro_expansion: bool, +} + +impl LateLintPass<'_> for UndocumentedUnsafeBlocks { + fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) { + if_chain! { + if !self.local_checked; + if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id); + if !in_external_macro(cx.tcx.sess, block.span); + if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules; + if let Some(enclosing_scope_hir_id) = cx.tcx.hir().get_enclosing_scope(block.hir_id); + if self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, block.span) == Some(false); + then { + let mut span = block.span; + + if let Some(local_span) = self.local_span { + span = local_span; + + let result = self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, span); + + if result.unwrap_or(true) { + self.local_checked = true; + return; + } + } + + self.lint(cx, span); + } + } + } + + fn check_local(&mut self, cx: &LateContext<'_>, local: &'_ Local<'_>) { + if_chain! { + if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, local.hir_id); + if !in_external_macro(cx.tcx.sess, local.span); + if let Some(init) = local.init; + then { + self.visit_expr(init); + + if self.local_level > 0 { + self.local_span = Some(local.span); + } + } + } + } + + fn check_block_post(&mut self, _: &LateContext<'_>, _: &'_ Block<'_>) { + self.local_level = self.local_level.saturating_sub(1); + + if self.local_level == 0 { + self.local_checked = false; + self.local_span = None; + } + } +} + +impl<'hir> Visitor<'hir> for UndocumentedUnsafeBlocks { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'v Expr<'v>) { + match ex.kind { + ExprKind::Block(_, _) => self.local_level = self.local_level.saturating_add(1), + _ => walk_expr(self, ex), + } + } +} + +impl UndocumentedUnsafeBlocks { + fn block_has_safety_comment(&mut self, tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span: Span) -> Option<bool> { + let map = tcx.hir(); + let source_map = tcx.sess.source_map(); + + let enclosing_scope_span = map.opt_span(enclosing_hir_id)?; + + let between_span = if in_macro(block_span) { + self.macro_expansion = true; + enclosing_scope_span.with_hi(block_span.hi()) + } else { + self.macro_expansion = false; + enclosing_scope_span.to(block_span) + }; + + let file_name = source_map.span_to_filename(between_span); + let source_file = source_map.get_source_file(&file_name)?; + + let lex_start = (between_span.lo().0 + 1) as usize; + let src_str = source_file.src.as_ref()?[lex_start..between_span.hi().0 as usize].to_string(); + + let mut pos = 0; + let mut comment = false; + + for token in rustc_lexer::tokenize(&src_str) { + match token.kind { + TokenKind::LineComment { doc_style: None } + | TokenKind::BlockComment { + doc_style: None, + terminated: true, + } => { + let comment_str = src_str[pos + 2..pos + token.len].to_ascii_uppercase(); + + if comment_str.contains("SAFETY:") { + comment = true; + } + }, + // We need to add all whitespace to `pos` before checking the comment's line number + TokenKind::Whitespace => {}, + _ => { + 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())) + .0; + // Find the block/local's line number + let block_line_num = tcx.sess.source_map().lookup_char_pos(block_span.lo()).line; + + // Check the comment is immediately followed by the block/local + if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num { + return Some(true); + } + + comment = false; + } + }, + } + + pos += token.len; + } + + Some(false) + } + + fn lint(&self, cx: &LateContext<'_>, mut span: Span) { + let source_map = cx.tcx.sess.source_map(); + + if source_map.is_multiline(span) { + span = source_map.span_until_char(span, '\n'); + } + + if self.macro_expansion { + span_lint_and_help( + cx, + UNDOCUMENTED_UNSAFE_BLOCKS, + span, + "unsafe block in macro expansion missing a safety comment", + None, + "consider adding a safety comment in the macro definition", + ); + } else { + let block_indent = indent_of(cx, span); + let suggestion = format!("// Safety: ...\n{}", snippet(cx, span, "..")); + + span_lint_and_sugg( + cx, + UNDOCUMENTED_UNSAFE_BLOCKS, + span, + "unsafe block missing a safety comment", + "consider adding a safety comment", + reindent_multiline(Cow::Borrowed(&suggestion), true, block_indent).to_string(), + Applicability::HasPlaceholders, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs new file mode 100644 index 00000000000..f3e8b688105 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs @@ -0,0 +1,223 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; +use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; +use clippy_utils::{is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq}; +use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, Span}; + +// TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std +declare_clippy_lint! { + /// ### What it does + /// Checks for `set_len()` call that creates `Vec` with uninitialized elements. + /// This is commonly caused by calling `set_len()` right after allocating or + /// reserving a buffer with `new()`, `default()`, `with_capacity()`, or `reserve()`. + /// + /// ### Why is this bad? + /// It creates a `Vec` with uninitialized data, which leads to + /// undefined behavior with most safe operations. Notably, uninitialized + /// `Vec<u8>` must not be used with generic `Read`. + /// + /// Moreover, calling `set_len()` on a `Vec` created with `new()` or `default()` + /// creates out-of-bound values that lead to heap memory corruption when used. + /// + /// ### Known Problems + /// This lint only checks directly adjacent statements. + /// + /// ### Example + /// ```rust,ignore + /// let mut vec: Vec<u8> = Vec::with_capacity(1000); + /// unsafe { vec.set_len(1000); } + /// reader.read(&mut vec); // undefined behavior! + /// ``` + /// + /// ### How to fix? + /// 1. Use an initialized buffer: + /// ```rust,ignore + /// let mut vec: Vec<u8> = vec![0; 1000]; + /// reader.read(&mut vec); + /// ``` + /// 2. Wrap the content in `MaybeUninit`: + /// ```rust,ignore + /// let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000); + /// vec.set_len(1000); // `MaybeUninit` can be uninitialized + /// ``` + /// 3. If you are on nightly, `Vec::spare_capacity_mut()` is available: + /// ```rust,ignore + /// let mut vec: Vec<u8> = Vec::with_capacity(1000); + /// let remaining = vec.spare_capacity_mut(); // `&mut [MaybeUninit<u8>]` + /// // perform initialization with `remaining` + /// vec.set_len(...); // Safe to call `set_len()` on initialized part + /// ``` + pub UNINIT_VEC, + correctness, + "Vec with uninitialized data" +} + +declare_lint_pass!(UninitVec => [UNINIT_VEC]); + +// FIXME: update to a visitor-based implementation. +// Threads: https://github.com/rust-lang/rust-clippy/pull/7682#discussion_r710998368 +impl<'tcx> LateLintPass<'tcx> for UninitVec { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + if !in_external_macro(cx.tcx.sess, block.span) { + for w in block.stmts.windows(2) { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = w[1].kind { + handle_uninit_vec_pair(cx, &w[0], expr); + } + } + + if let (Some(stmt), Some(expr)) = (block.stmts.last(), block.expr) { + handle_uninit_vec_pair(cx, stmt, expr); + } + } + } +} + +fn handle_uninit_vec_pair( + cx: &LateContext<'tcx>, + maybe_init_or_reserve: &'tcx Stmt<'tcx>, + maybe_set_len: &'tcx Expr<'tcx>, +) { + if_chain! { + if let Some(vec) = extract_init_or_reserve_target(cx, maybe_init_or_reserve); + if let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len); + if vec.location.eq_expr(cx, set_len_self); + if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind(); + if let ty::Adt(_, substs) = vec_ty.kind(); + // `#[allow(...)]` attribute can be set on enclosing unsafe block of `set_len()` + if !is_lint_allowed(cx, UNINIT_VEC, maybe_set_len.hir_id); + then { + if vec.has_capacity() { + // with_capacity / reserve -> set_len + + // Check T of Vec<T> + if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)) { + // FIXME: #7698, false positive of the internal lints + #[allow(clippy::collapsible_span_lint_calls)] + span_lint_and_then( + cx, + UNINIT_VEC, + vec![call_span, maybe_init_or_reserve.span], + "calling `set_len()` immediately after reserving a buffer creates uninitialized values", + |diag| { + diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); + }, + ); + } + } else { + // new / default -> set_len + span_lint( + cx, + UNINIT_VEC, + vec![call_span, maybe_init_or_reserve.span], + "calling `set_len()` on empty `Vec` creates out-of-bound values", + ); + } + } + } +} + +/// The target `Vec` that is initialized or reserved +#[derive(Clone, Copy)] +struct TargetVec<'tcx> { + location: VecLocation<'tcx>, + /// `None` if `reserve()` + init_kind: Option<VecInitKind>, +} + +impl TargetVec<'_> { + pub fn has_capacity(self) -> bool { + !matches!(self.init_kind, Some(VecInitKind::New | VecInitKind::Default)) + } +} + +#[derive(Clone, Copy)] +enum VecLocation<'tcx> { + Local(HirId), + Expr(&'tcx Expr<'tcx>), +} + +impl<'tcx> VecLocation<'tcx> { + pub fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + match self { + VecLocation::Local(hir_id) => path_to_local_id(expr, hir_id), + VecLocation::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr), + } + } +} + +/// Finds the target location where the result of `Vec` initialization is stored +/// or `self` expression for `Vec::reserve()`. +fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option<TargetVec<'tcx>> { + match stmt.kind { + StmtKind::Local(local) => { + if_chain! { + if let Some(init_expr) = local.init; + if let PatKind::Binding(_, hir_id, _, None) = local.pat.kind; + if let Some(init_kind) = get_vec_init_kind(cx, init_expr); + then { + return Some(TargetVec { + location: VecLocation::Local(hir_id), + init_kind: Some(init_kind), + }) + } + } + }, + StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind { + ExprKind::Assign(lhs, rhs, _span) => { + if let Some(init_kind) = get_vec_init_kind(cx, rhs) { + return Some(TargetVec { + location: VecLocation::Expr(lhs), + init_kind: Some(init_kind), + }); + } + }, + ExprKind::MethodCall(path, _, [self_expr, _], _) if is_reserve(cx, path, self_expr) => { + return Some(TargetVec { + location: VecLocation::Expr(self_expr), + init_kind: None, + }); + }, + _ => (), + }, + StmtKind::Item(_) => (), + } + None +} + +fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_>) -> bool { + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::Vec) + && path.ident.name.as_str() == "reserve" +} + +/// Returns self if the expression is `Vec::set_len()` +fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> { + // peel unsafe blocks in `unsafe { vec.set_len() }` + let expr = peel_hir_expr_while(expr, |e| { + if let ExprKind::Block(block, _) = e.kind { + // Extract the first statement/expression + match (block.stmts.get(0).map(|stmt| &stmt.kind), block.expr) { + (None, Some(expr)) => Some(expr), + (Some(StmtKind::Expr(expr) | StmtKind::Semi(expr)), _) => Some(expr), + _ => None, + } + } else { + None + } + }); + match expr.kind { + ExprKind::MethodCall(path, _, [self_expr, _], _) => { + let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs(); + if is_type_diagnostic_item(cx, self_type, sym::Vec) && path.ident.name.as_str() == "set_len" { + Some((self_expr, expr.span)) + } else { + None + } + }, + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs index dd74bf367f3..26b56e0f2f3 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; @@ -193,10 +193,15 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> { let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; + if_chain! { if let ExprKind::Path(QPath::Resolved(_, Path { segments: [PathSegment { ident: left_name, .. }], .. - })) = &left_expr.kind { - if left_name == left_ident { + })) = &left_expr.kind; + if left_name == left_ident; + if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| { + implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]) + }); + then { return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })); } } diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 6cbada4c150..d05c52122d5 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -23,6 +23,14 @@ pub enum DisallowedMethod { WithReason { path: String, reason: Option<String> }, } +/// A single disallowed type, used by the `DISALLOWED_TYPE` lint. +#[derive(Clone, Debug, Deserialize)] +#[serde(untagged)] +pub enum DisallowedType { + Simple(String), + WithReason { path: String, reason: Option<String> }, +} + /// Conf with parse errors #[derive(Default)] pub struct TryConf { @@ -255,7 +263,7 @@ define_Conf! { /// Lint: DISALLOWED_TYPE. /// /// The list of disallowed types, written as fully qualified paths. - (disallowed_types: Vec<String> = Vec::new()), + (disallowed_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()), /// Lint: UNREADABLE_LITERAL. /// /// Should the fraction of a decimal be linted to include separators. diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index 9f9edbf258a..824ec53ab9c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -770,8 +770,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); if let Some(ty_did) = path_to_res(cx, &segments[..]).opt_def_id(); // Check if the matched type is a diagnostic item - let diag_items = cx.tcx.diagnostic_items(ty_did.krate); - if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None }); + if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did); then { // TODO: check paths constants from external crates. let cx_snippet = snippet(cx, context.span, "_"); diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index d124d948b5e..d3234b5758a 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -63,13 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg))); then { // report the error around the `vec!` not inside `<std macros>:` - let span = arg.span - .ctxt() - .outer_expn_data() - .call_site - .ctxt() - .outer_expn_data() - .call_site; + let span = arg.span.ctxt().outer_expn_data().call_site; self.check_vec_macro(cx, &vec_args, Mutability::Not, span); } } diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs index d8e241d72af..b92b6ca4f43 100644 --- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -1,16 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_def_path, path_to_local, path_to_local_id, paths}; +use clippy_utils::{path_to_local, path_to_local_id}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{symbol::sym, Span}; -use std::convert::TryInto; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -41,11 +39,6 @@ pub struct VecInitThenPush { searcher: Option<VecPushSearcher>, } -#[derive(Clone, Copy)] -enum VecInitKind { - New, - WithCapacity(u64), -} struct VecPushSearcher { local_id: HirId, init: VecInitKind, @@ -58,7 +51,8 @@ impl VecPushSearcher { fn display_err(&self, cx: &LateContext<'_>) { match self.init { _ if self.found == 0 => return, - VecInitKind::WithCapacity(x) if x > self.found => return, + VecInitKind::WithLiteralCapacity(x) if x > self.found => return, + VecInitKind::WithExprCapacity(_) => return, _ => (), }; @@ -152,37 +146,3 @@ impl LateLintPass<'_> for VecInitThenPush { } } } - -fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<VecInitKind> { - if let ExprKind::Call(func, args) = expr.kind { - match func.kind { - ExprKind::Path(QPath::TypeRelative(ty, name)) - if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) => - { - if name.ident.name == sym::new { - return Some(VecInitKind::New); - } else if name.ident.name.as_str() == "with_capacity" { - return args.get(0).and_then(|arg| { - if_chain! { - if let ExprKind::Lit(lit) = &arg.kind; - if let LitKind::Int(num, _) = lit.node; - then { - Some(VecInitKind::WithCapacity(num.try_into().ok()?)) - } else { - None - } - } - }); - } - } - ExprKind::Path(QPath::Resolved(_, path)) - if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) => - { - return Some(VecInitKind::New); - } - _ => (), - } - } - None -} diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index e7fca3ae5d4..d99a3d9359e 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.57" +version = "0.1.58" edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index ba4d50bf744..60c4cb361aa 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -2,13 +2,16 @@ #![deny(clippy::missing_docs_in_private_items)] -use crate::{is_expn_of, match_def_path, paths}; +use crate::ty::is_type_diagnostic_item; +use crate::{is_expn_of, last_path_segment, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{self, LitKind}; use rustc_hir as hir; -use rustc_hir::{Arm, Block, BorrowKind, Expr, ExprKind, LoopSource, MatchSource, Node, Pat, StmtKind, UnOp}; +use rustc_hir::{ + Arm, Block, BorrowKind, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StmtKind, UnOp, +}; use rustc_lint::LateContext; -use rustc_span::{sym, ExpnKind, Span, Symbol}; +use rustc_span::{sym, symbol, ExpnKind, Span, Symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`. @@ -569,6 +572,106 @@ impl FormatArgsExpn<'tcx> { } } } + + /// Returns a vector of `FormatArgsArg`. + pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> { + if let Some(expr) = self.fmt_expr { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; + if let ExprKind::Array(exprs) = expr.kind; + then { + exprs.iter().map(|fmt| { + if_chain! { + // struct `core::fmt::rt::v1::Argument` + if let ExprKind::Struct(_, fields, _) = fmt.kind; + if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position); + if let ExprKind::Lit(lit) = &position_field.expr.kind; + if let LitKind::Int(position, _) = lit.node; + then { + let i = usize::try_from(position).unwrap(); + Some(FormatArgsArg { value: self.value_args[i], arg: &self.args[i], fmt: Some(fmt) }) + } else { + None + } + } + }).collect() + } else { + None + } + } + } else { + Some( + self.value_args + .iter() + .zip(self.args.iter()) + .map(|(value, arg)| FormatArgsArg { value, arg, fmt: None }) + .collect(), + ) + } + } +} + +/// Type representing a `FormatArgsExpn`'s format arguments +pub struct FormatArgsArg<'tcx> { + /// An element of `value_args` according to `position` + pub value: &'tcx Expr<'tcx>, + /// An element of `args` according to `position` + pub arg: &'tcx Expr<'tcx>, + /// An element of `fmt_expn` + pub fmt: Option<&'tcx Expr<'tcx>>, +} + +impl<'tcx> FormatArgsArg<'tcx> { + /// Returns true if any formatting parameters are used that would have an effect on strings, + /// like `{:+2}` instead of just `{}`. + pub fn has_string_formatting(&self) -> bool { + self.fmt.map_or(false, |fmt| { + // `!` because these conditions check that `self` is unformatted. + !if_chain! { + // struct `core::fmt::rt::v1::Argument` + if let ExprKind::Struct(_, fields, _) = fmt.kind; + if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format); + // struct `core::fmt::rt::v1::FormatSpec` + if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind; + let mut precision_found = false; + let mut width_found = false; + if subfields.iter().all(|field| { + match field.ident.name { + sym::precision => { + precision_found = true; + if let ExprKind::Path(ref precision_path) = field.expr.kind { + last_path_segment(precision_path).ident.name == sym::Implied + } else { + false + } + } + sym::width => { + width_found = true; + if let ExprKind::Path(ref width_qpath) = field.expr.kind { + last_path_segment(width_qpath).ident.name == sym::Implied + } else { + false + } + } + _ => true, + } + }); + if precision_found && width_found; + then { true } else { false } + } + }) + } + + /// Returns true if the argument is formatted using `Display::fmt`. + pub fn is_display(&self) -> bool { + if_chain! { + if let ExprKind::Call(_, [_, format_field]) = self.arg.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = format_field.kind; + if let [.., t, _] = path.segments; + if t.ident.name == sym::Display; + then { true } else { false } + } + } } /// Checks if a `let` statement is from a `for` loop desugaring. @@ -619,7 +722,6 @@ impl PanicExpn<'tcx> { if let Some(init) = block.expr; if let ExprKind::Call(_, [format_args]) = init.kind; let expn_data = expr.span.ctxt().outer_expn_data(); - if let ExprKind::AddrOf(_, _, format_args) = format_args.kind; if let Some(format_args) = FormatArgsExpn::parse(format_args); then { Some(PanicExpn { @@ -632,3 +734,51 @@ impl PanicExpn<'tcx> { } } } + +/// A parsed `Vec` initialization expression +#[derive(Clone, Copy)] +pub enum VecInitKind { + /// `Vec::new()` + New, + /// `Vec::default()` or `Default::default()` + Default, + /// `Vec::with_capacity(123)` + WithLiteralCapacity(u64), + /// `Vec::with_capacity(slice.len())` + WithExprCapacity(HirId), +} + +/// Checks if given expression is an initialization of `Vec` and returns its kind. +pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<VecInitKind> { + if let ExprKind::Call(func, args) = expr.kind { + match func.kind { + ExprKind::Path(QPath::TypeRelative(ty, name)) + if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) => + { + if name.ident.name == sym::new { + return Some(VecInitKind::New); + } else if name.ident.name == symbol::kw::Default { + return Some(VecInitKind::Default); + } else if name.ident.name.as_str() == "with_capacity" { + let arg = args.get(0)?; + if_chain! { + if let ExprKind::Lit(lit) = &arg.kind; + if let LitKind::Int(num, _) = lit.node; + then { + return Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?)) + } + } + return Some(VecInitKind::WithExprCapacity(arg.hir_id)); + } + } + ExprKind::Path(QPath::Resolved(_, path)) + if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) => + { + return Some(VecInitKind::Default); + } + _ => (), + } + } + None +} diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index c47aa9170e5..9bc380ca6ca 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -69,11 +69,13 @@ 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::itemlikevisit::ItemLikeVisitor; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, - ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, Param, Pat, - PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, + def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs, + HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, + Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, + UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -251,11 +253,7 @@ pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem /// Returns `true` if this `span` was expanded by any macro. #[must_use] pub fn in_macro(span: Span) -> bool { - if span.from_expansion() { - !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) - } else { - false - } + span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) } pub fn is_unit_expr(expr: &Expr<'_>) -> bool { @@ -1285,10 +1283,9 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool } let enclosing_body = cx.tcx.hir().local_def_id(cx.tcx.hir().enclosing_body_owner(e.hir_id)); if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) { - value == v - } else { - false + return value == v; } + false } /// Checks whether the given expression is a constant literal of the given value. @@ -1315,7 +1312,7 @@ pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// Returns the pre-expansion span if is this comes from an expansion of the /// macro `name`. -/// See also `is_direct_expn_of`. +/// See also [`is_direct_expn_of`]. #[must_use] pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> { loop { @@ -1338,13 +1335,13 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> { /// Returns the pre-expansion span if the span directly comes from an expansion /// of the macro `name`. -/// The difference with `is_expn_of` is that in -/// ```rust,ignore +/// The difference with [`is_expn_of`] is that in +/// ```rust +/// # macro_rules! foo { ($e:tt) => { $e } }; macro_rules! bar { ($e:expr) => { $e } } /// foo!(bar!(42)); /// ``` /// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only -/// `bar!` by -/// `is_direct_expn_of`. +/// from `bar!` by `is_direct_expn_of`. #[must_use] pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> { if span.from_expansion() { @@ -1467,11 +1464,9 @@ pub fn is_self(slf: &Param<'_>) -> bool { } pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool { - if_chain! { - if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind; - if let Res::SelfTy(..) = path.res; - then { - return true + if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind { + if let Res::SelfTy(..) = path.res { + return true; } } false @@ -1646,7 +1641,6 @@ pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool { did, &[ &paths::BEGIN_PANIC, - &paths::BEGIN_PANIC_FMT, &paths::PANIC_ANY, &paths::PANICKING_PANIC, &paths::PANICKING_PANIC_FMT, @@ -2063,27 +2057,80 @@ macro_rules! unwrap_cargo_metadata { } pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { - if_chain! { - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind; - if let Res::Def(_, def_id) = path.res; - then { - cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) - } else { - false + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + if let Res::Def(_, def_id) = path.res { + return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr); } } + false } -/// Checks whether item either has `test` attribute applied, or -/// is a module with `test` in its name. -pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool { - if let Some(def_id) = tcx.hir().opt_local_def_id(item.hir_id()) { - if tcx.has_attr(def_id.to_def_id(), sym::test) { - return true; +struct VisitConstTestStruct<'tcx> { + tcx: TyCtxt<'tcx>, + names: Vec<Symbol>, + found: bool, +} +impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'hir> { + fn visit_item(&mut self, item: &Item<'_>) { + if let ItemKind::Const(ty, _body) = item.kind { + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + // We could also check for the type name `test::TestDescAndFn` + // and the `#[rustc_test_marker]` attribute? + if let Res::Def(DefKind::Struct, _) = path.res { + let has_test_marker = self + .tcx + .hir() + .attrs(item.hir_id()) + .iter() + .any(|a| a.has_name(sym::rustc_test_marker)); + if has_test_marker && self.names.contains(&item.ident.name) { + self.found = true; + } + } + } } } + fn visit_trait_item(&mut self, _: &TraitItem<'_>) {} + fn visit_impl_item(&mut self, _: &ImplItem<'_>) {} + fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {} +} + +/// Checks if the function containing the given `HirId` is a `#[test]` function +/// +/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`. +pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { + let names: Vec<_> = tcx + .hir() + .parent_iter(id) + // Since you can nest functions we need to collect all until we leave + // function scope + .filter_map(|(_id, node)| { + if let Node::Item(item) = node { + if let ItemKind::Fn(_, _, _) = item.kind { + return Some(item.ident.name); + } + } + None + }) + .collect(); + let parent_mod = tcx.parent_module(id); + let mut vis = VisitConstTestStruct { + tcx, + names, + found: false, + }; + tcx.hir().visit_item_likes_in_module(parent_mod, &mut vis); + vis.found +} - matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") +/// Checks whether item either has `test` attribute appelied, or +/// is a module with `test` in its name. +/// +/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`. +pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool { + is_in_test_function(tcx, item.hir_id()) + || matches!(item.kind, ItemKind::Mod(..)) + && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests") } macro_rules! op_utils { diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index e43c5756021..501b08a47f1 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -17,10 +17,15 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ #[cfg(feature = "metadata-collector-lint")] pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const ASSERT_EQ_MACRO: [&str; 3] = ["core", "macros", "assert_eq"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const ASSERT_MACRO: [&str; 4] = ["core", "macros", "builtin", "assert"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const ASSERT_NE_MACRO: [&str; 3] = ["core", "macros", "assert_ne"]; pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"]; -pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"]; /// Preferably use the diagnostic item `sym::Borrow` where possible pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; @@ -42,11 +47,17 @@ pub const DROP: [&str; 3] = ["core", "mem", "drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; #[cfg(feature = "internal-lints")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const EPRINT_MACRO: [&str; 3] = ["std", "macros", "eprint"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const EPRINTLN_MACRO: [&str; 3] = ["std", "macros", "eprintln"]; pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"]; pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"]; pub const FILE: [&str; 3] = ["std", "fs", "File"]; pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const FORMAT_ARGS_MACRO: [&str; 4] = ["core", "macros", "builtin", "format_args"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"]; pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; @@ -109,6 +120,10 @@ pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "Permis pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const PRINT_MACRO: [&str; 3] = ["std", "macros", "print"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +pub const PRINTLN_MACRO: [&str; 3] = ["std", "macros", "println"]; pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"]; pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; @@ -185,3 +200,7 @@ pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"]; pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"]; +#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros +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"]; diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 6ebe1a0028a..ca64ac7de3e 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -367,3 +367,13 @@ pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { _ => a == b, } } + +/// Checks if a given type looks safe to be uninitialized. +pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component), + ty::Tuple(types) => types.types().all(|ty| is_uninit_value_valid_for_ty(cx, ty)), + ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did), + _ => false, + } +} diff --git a/src/tools/clippy/doc/basics.md b/src/tools/clippy/doc/basics.md index ff2e0417435..57a90a924ec 100644 --- a/src/tools/clippy/doc/basics.md +++ b/src/tools/clippy/doc/basics.md @@ -96,6 +96,7 @@ cargo dev setup git-hook # (experimental) Setup Clippy to work with IntelliJ-Rust cargo dev setup intellij ``` +More about intellij command usage and reasons [here](../CONTRIBUTING.md#intellij-rust) ## lintcheck `cargo lintcheck` will build and run clippy on a fixed set of crates and generate a log of the results. diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index f98819303e6..67eaf286004 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-10-07" +channel = "nightly-2021-10-21" components = ["llvm-tools-preview", "rustc-dev", "rust-src"] diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index e8b1640c869..c15835ef299 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -149,6 +149,19 @@ fn run_ui(cfg: &mut compiletest::Config) { compiletest::run_tests(cfg); } +fn run_ui_test(cfg: &mut compiletest::Config) { + cfg.mode = TestMode::Ui; + cfg.src_base = Path::new("tests").join("ui_test"); + let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap()); + let rustcflags = cfg.target_rustcflags.get_or_insert_with(Default::default); + let len = rustcflags.len(); + rustcflags.push_str(" --test"); + compiletest::run_tests(cfg); + if let Some(ref mut flags) = &mut cfg.target_rustcflags { + flags.truncate(len); + } +} + fn run_internal_tests(cfg: &mut compiletest::Config) { // only run internal tests with the internal-tests feature if !RUN_INTERNAL_TESTS { @@ -312,6 +325,7 @@ fn compile_test() { prepare_env(); let mut config = default_config(); run_ui(&mut config); + run_ui_test(&mut config); run_ui_toml(&mut config); run_ui_cargo(&mut config); run_internal_tests(&mut config); diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr index 8e04d447fbc..12e05eaa7a0 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr +++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr @@ -27,12 +27,13 @@ error: unnecessary `Symbol` to string conversion --> $DIR/unnecessary_symbol_str.rs:14:5 | LL | &*Ident::empty().as_str() == "clippy"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` error: unnecessary `Symbol` to string conversion --> $DIR/unnecessary_symbol_str.rs:15:5 | LL | "clippy" == Ident::empty().to_string(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_type/clippy.toml index dac4446703b..6cb9e2ef954 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_type/clippy.toml @@ -7,5 +7,9 @@ disallowed-types = [ "std::time::Instant", "std::io::Read", "std::primitive::usize", - "bool" + "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" }, ] diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs index 0871a3073ab..410f0765055 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs @@ -25,6 +25,10 @@ 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(); diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr index 90ce7db2cc4..08a400a8367 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr @@ -60,59 +60,73 @@ error: `usize` is not allowed according to config 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:30:48 + --> $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:30:12 + --> $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:31:13 + --> $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:32:13 + --> $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:33:17 + --> $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:33:48 + --> $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:34:43 + --> $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:35:13 + --> $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:37:12 + --> $DIR/conf_disallowed_type.rs:41:12 | LL | let _: usize = 64_usize; | ^^^^^ -error: aborting due to 19 previous errors +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs index 170955e726c..0251fada9e8 100644 --- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -113,3 +113,10 @@ macro_rules! default_numeric_fallback { let x = 22; }; } + +#[macro_export] +macro_rules! mut_mut { + () => { + let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32; + }; +} diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed index 4e583a25b94..061a4ab9b2e 100644 --- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed @@ -1,7 +1,7 @@ // run-rustfix #![feature(stmt_expr_attributes)] -#![allow(unused, clippy::no_effect)] +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] // This doesn't get linted, see known problems diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs index 9c0fcf6fb45..035169fab85 100644 --- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs @@ -1,7 +1,7 @@ // run-rustfix #![feature(stmt_expr_attributes)] -#![allow(unused, clippy::no_effect)] +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] // This doesn't get linted, see known problems diff --git a/src/tools/clippy/tests/ui/doc_unsafe.rs b/src/tools/clippy/tests/ui/doc_unsafe.rs index 8f823f1672b..03bb30f9083 100644 --- a/src/tools/clippy/tests/ui/doc_unsafe.rs +++ b/src/tools/clippy/tests/ui/doc_unsafe.rs @@ -115,3 +115,13 @@ fn main() { drive(); } } + +// do not lint if any parent has `#[doc(hidden)]` attribute +// see #7347 +#[doc(hidden)] +pub mod __macro { + pub struct T; + impl T { + pub unsafe fn f() {} + } +} diff --git a/src/tools/clippy/tests/ui/equatable_if_let.fixed b/src/tools/clippy/tests/ui/equatable_if_let.fixed index ba72cc237b4..88918d9671e 100644 --- a/src/tools/clippy/tests/ui/equatable_if_let.fixed +++ b/src/tools/clippy/tests/ui/equatable_if_let.fixed @@ -66,4 +66,13 @@ fn main() { if g == NotStructuralEq::A {} if let Some(NotPartialEq::A) = Some(f) {} if Some(g) == Some(NotStructuralEq::A) {} + + macro_rules! m1 { + (x) => { + "abc" + }; + } + if "abc" == m1!(x) { + println!("OK"); + } } diff --git a/src/tools/clippy/tests/ui/equatable_if_let.rs b/src/tools/clippy/tests/ui/equatable_if_let.rs index 12526ca193d..9a7ab75ef45 100644 --- a/src/tools/clippy/tests/ui/equatable_if_let.rs +++ b/src/tools/clippy/tests/ui/equatable_if_let.rs @@ -66,4 +66,13 @@ fn main() { if let NotStructuralEq::A = g {} if let Some(NotPartialEq::A) = Some(f) {} if let Some(NotStructuralEq::A) = Some(g) {} + + macro_rules! m1 { + (x) => { + "abc" + }; + } + if let m1!(x) = "abc" { + println!("OK"); + } } diff --git a/src/tools/clippy/tests/ui/equatable_if_let.stderr b/src/tools/clippy/tests/ui/equatable_if_let.stderr index 79ef919384d..760ff88f448 100644 --- a/src/tools/clippy/tests/ui/equatable_if_let.stderr +++ b/src/tools/clippy/tests/ui/equatable_if_let.stderr @@ -60,5 +60,11 @@ error: this pattern matching can be expressed using equality LL | if let Some(NotStructuralEq::A) = Some(g) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)` -error: aborting due to 10 previous errors +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:75:8 + | +LL | if let m1!(x) = "abc" { + | ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/expect_fun_call.fixed b/src/tools/clippy/tests/ui/expect_fun_call.fixed index a756d1cf506..cf923a6a594 100644 --- a/src/tools/clippy/tests/ui/expect_fun_call.fixed +++ b/src/tools/clippy/tests/ui/expect_fun_call.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::expect_fun_call)] +#![allow(clippy::to_string_in_format_args)] /// Checks implementation of the `EXPECT_FUN_CALL` lint diff --git a/src/tools/clippy/tests/ui/expect_fun_call.rs b/src/tools/clippy/tests/ui/expect_fun_call.rs index 60bbaa89d42..e6f252259df 100644 --- a/src/tools/clippy/tests/ui/expect_fun_call.rs +++ b/src/tools/clippy/tests/ui/expect_fun_call.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::expect_fun_call)] +#![allow(clippy::to_string_in_format_args)] /// Checks implementation of the `EXPECT_FUN_CALL` lint diff --git a/src/tools/clippy/tests/ui/expect_fun_call.stderr b/src/tools/clippy/tests/ui/expect_fun_call.stderr index 6dc796f5cee..ac48a06671c 100644 --- a/src/tools/clippy/tests/ui/expect_fun_call.stderr +++ b/src/tools/clippy/tests/ui/expect_fun_call.stderr @@ -1,5 +1,5 @@ error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:28:26 + --> $DIR/expect_fun_call.rs:29:26 | LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` @@ -7,67 +7,67 @@ LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code = note: `-D clippy::expect-fun-call` implied by `-D warnings` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:31:26 + --> $DIR/expect_fun_call.rs:32:26 | LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:41:25 + --> $DIR/expect_fun_call.rs:42:25 | LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:44:25 + --> $DIR/expect_fun_call.rs:45:25 | LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:56:17 + --> $DIR/expect_fun_call.rs:57:17 | LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:77:21 + --> $DIR/expect_fun_call.rs:78:21 | LL | Some("foo").expect(&get_string()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:78:21 + --> $DIR/expect_fun_call.rs:79:21 | LL | Some("foo").expect(get_string().as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:79:21 + --> $DIR/expect_fun_call.rs:80:21 | LL | Some("foo").expect(get_string().as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:81:21 + --> $DIR/expect_fun_call.rs:82:21 | LL | Some("foo").expect(get_static_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_static_str()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:82:21 + --> $DIR/expect_fun_call.rs:83:21 | LL | Some("foo").expect(get_non_static_str(&0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:86:16 + --> $DIR/expect_fun_call.rs:87:16 | LL | Some(true).expect(&format!("key {}, {}", 1, 2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:92:17 + --> $DIR/expect_fun_call.rs:93:17 | LL | opt_ref.expect(&format!("{:?}", opt_ref)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))` diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.rs b/src/tools/clippy/tests/ui/field_reassign_with_default.rs index 787053fb000..7367910eaa1 100644 --- a/src/tools/clippy/tests/ui/field_reassign_with_default.rs +++ b/src/tools/clippy/tests/ui/field_reassign_with_default.rs @@ -183,3 +183,67 @@ struct WrapperMulti<T, U> { i: T, j: U, } + +mod issue6312 { + use std::sync::atomic::AtomicBool; + use std::sync::Arc; + + // do not lint: type implements `Drop` but not all fields are `Copy` + #[derive(Clone, Default)] + pub struct ImplDropNotAllCopy { + name: String, + delay_data_sync: Arc<AtomicBool>, + } + + impl Drop for ImplDropNotAllCopy { + fn drop(&mut self) { + self.close() + } + } + + impl ImplDropNotAllCopy { + fn new(name: &str) -> Self { + let mut f = ImplDropNotAllCopy::default(); + f.name = name.to_owned(); + f + } + fn close(&self) {} + } + + // lint: type implements `Drop` and all fields are `Copy` + #[derive(Clone, Default)] + pub struct ImplDropAllCopy { + name: usize, + delay_data_sync: bool, + } + + impl Drop for ImplDropAllCopy { + fn drop(&mut self) { + self.close() + } + } + + impl ImplDropAllCopy { + fn new(name: &str) -> Self { + let mut f = ImplDropAllCopy::default(); + f.name = name.len(); + f + } + fn close(&self) {} + } + + // lint: type does not implement `Drop` though all fields are `Copy` + #[derive(Clone, Default)] + pub struct NoDropAllCopy { + name: usize, + delay_data_sync: bool, + } + + impl NoDropAllCopy { + fn new(name: &str) -> Self { + let mut f = NoDropAllCopy::default(); + f.name = name.len(); + f + } + } +} diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr index b56db08ec8a..3ce4b91a548 100644 --- a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr +++ b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr @@ -107,5 +107,29 @@ note: consider initializing the variable with `WrapperMulti::<i32, i64> { i: 42, LL | let mut a: WrapperMulti<i32, i64> = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:229:13 + | +LL | f.name = name.len(); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `issue6312::ImplDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:228:13 + | +LL | let mut f = ImplDropAllCopy::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:245:13 + | +LL | f.name = name.len(); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `issue6312::NoDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:244:13 + | +LL | let mut f = NoDropAllCopy::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.rs new file mode 100644 index 00000000000..46704683926 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.rs @@ -0,0 +1,76 @@ +#![warn(clippy::fn_to_numeric_cast_any)] +#![allow(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] + +fn foo() -> u8 { + 0 +} + +fn generic_foo<T>(x: T) -> T { + x +} + +trait Trait { + fn static_method() -> u32 { + 2 + } +} + +struct Struct; + +impl Trait for Struct {} + +fn fn_pointer_to_integer() { + let _ = foo as i8; + let _ = foo as i16; + let _ = foo as i32; + let _ = foo as i64; + let _ = foo as i128; + let _ = foo as isize; + + let _ = foo as u8; + let _ = foo as u16; + let _ = foo as u32; + let _ = foo as u64; + let _ = foo as u128; + let _ = foo as usize; +} + +fn static_method_to_integer() { + let _ = Struct::static_method as usize; +} + +fn fn_with_fn_arg(f: fn(i32) -> u32) -> usize { + f as usize +} + +fn fn_with_generic_static_trait_method<T: Trait>() -> usize { + T::static_method as usize +} + +fn closure_to_fn_to_integer() { + let clos = |x| x * 2_u32; + + let _ = (clos as fn(u32) -> u32) as usize; +} + +fn fn_to_raw_ptr() { + let _ = foo as *const (); +} + +fn cast_fn_to_self() { + // Casting to the same function pointer type should be permitted. + let _ = foo as fn() -> u8; +} + +fn cast_generic_to_concrete() { + // Casting to a more concrete function pointer type should be permitted. + let _ = generic_foo as fn(usize) -> usize; +} + +fn cast_closure_to_fn() { + // Casting a closure to a function pointer should be permitted. + let id = |x| x; + let _ = id as fn(usize) -> usize; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr new file mode 100644 index 00000000000..a6c4a77672f --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr @@ -0,0 +1,106 @@ +error: casting function pointer `foo` to `i8` + --> $DIR/fn_to_numeric_cast_any.rs:23:13 + | +LL | let _ = foo as i8; + | ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i8` + | + = note: `-D clippy::fn-to-numeric-cast-any` implied by `-D warnings` + +error: casting function pointer `foo` to `i16` + --> $DIR/fn_to_numeric_cast_any.rs:24:13 + | +LL | let _ = foo as i16; + | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i16` + +error: casting function pointer `foo` to `i32` + --> $DIR/fn_to_numeric_cast_any.rs:25:13 + | +LL | let _ = foo as i32; + | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i32` + +error: casting function pointer `foo` to `i64` + --> $DIR/fn_to_numeric_cast_any.rs:26:13 + | +LL | let _ = foo as i64; + | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i64` + +error: casting function pointer `foo` to `i128` + --> $DIR/fn_to_numeric_cast_any.rs:27:13 + | +LL | let _ = foo as i128; + | ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i128` + +error: casting function pointer `foo` to `isize` + --> $DIR/fn_to_numeric_cast_any.rs:28:13 + | +LL | let _ = foo as isize; + | ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as isize` + +error: casting function pointer `foo` to `u8` + --> $DIR/fn_to_numeric_cast_any.rs:30:13 + | +LL | let _ = foo as u8; + | ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u8` + +error: casting function pointer `foo` to `u16` + --> $DIR/fn_to_numeric_cast_any.rs:31:13 + | +LL | let _ = foo as u16; + | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u16` + +error: casting function pointer `foo` to `u32` + --> $DIR/fn_to_numeric_cast_any.rs:32:13 + | +LL | let _ = foo as u32; + | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u32` + +error: casting function pointer `foo` to `u64` + --> $DIR/fn_to_numeric_cast_any.rs:33:13 + | +LL | let _ = foo as u64; + | ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u64` + +error: casting function pointer `foo` to `u128` + --> $DIR/fn_to_numeric_cast_any.rs:34:13 + | +LL | let _ = foo as u128; + | ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u128` + +error: casting function pointer `foo` to `usize` + --> $DIR/fn_to_numeric_cast_any.rs:35:13 + | +LL | let _ = foo as usize; + | ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as usize` + +error: casting function pointer `Struct::static_method` to `usize` + --> $DIR/fn_to_numeric_cast_any.rs:39:13 + | +LL | let _ = Struct::static_method as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `Struct::static_method() as usize` + +error: casting function pointer `f` to `usize` + --> $DIR/fn_to_numeric_cast_any.rs:43:5 + | +LL | f as usize + | ^^^^^^^^^^ help: did you mean to invoke the function?: `f() as usize` + +error: casting function pointer `T::static_method` to `usize` + --> $DIR/fn_to_numeric_cast_any.rs:47:5 + | +LL | T::static_method as usize + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `T::static_method() as usize` + +error: casting function pointer `(clos as fn(u32) -> u32)` to `usize` + --> $DIR/fn_to_numeric_cast_any.rs:53:13 + | +LL | let _ = (clos as fn(u32) -> u32) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `(clos as fn(u32) -> u32)() as usize` + +error: casting function pointer `foo` to `*const ()` + --> $DIR/fn_to_numeric_cast_any.rs:57:13 + | +LL | let _ = foo as *const (); + | ^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as *const ()` + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed index 5dd64140e81..73fc750511c 100644 --- a/src/tools/clippy/tests/ui/format.fixed +++ b/src/tools/clippy/tests/ui/format.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::print_literal, clippy::redundant_clone)] +#![allow(clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args)] #![warn(clippy::useless_format)] struct Foo(pub String); diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs index 4599fb5207e..2f4595650cb 100644 --- a/src/tools/clippy/tests/ui/format.rs +++ b/src/tools/clippy/tests/ui/format.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::print_literal, clippy::redundant_clone)] +#![allow(clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args)] #![warn(clippy::useless_format)] struct Foo(pub String); diff --git a/src/tools/clippy/tests/ui/format_args.fixed b/src/tools/clippy/tests/ui/format_args.fixed new file mode 100644 index 00000000000..8376566c4d6 --- /dev/null +++ b/src/tools/clippy/tests/ui/format_args.fixed @@ -0,0 +1,105 @@ +// run-rustfix + +#![allow(unreachable_code)] +#![allow(unused_macros)] +#![allow(unused_variables)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::eq_op)] +#![warn(clippy::to_string_in_format_args)] + +use std::io::{stdout, Write}; +use std::ops::Deref; +use std::panic::Location; + +struct Somewhere; + +impl ToString for Somewhere { + fn to_string(&self) -> String { + String::from("somewhere") + } +} + +struct X(u32); + +impl Deref for X { + type Target = u32; + + fn deref(&self) -> &u32 { + &self.0 + } +} + +struct Y<'a>(&'a X); + +impl<'a> Deref for Y<'a> { + type Target = &'a X; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +struct Z(u32); + +impl Deref for Z { + type Target = u32; + + fn deref(&self) -> &u32 { + &self.0 + } +} + +impl std::fmt::Display for Z { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Z") + } +} + +macro_rules! my_macro { + () => { + // here be dragons, do not enter (or lint) + println!("error: something failed at {}", Location::caller().to_string()); + }; +} + +macro_rules! my_other_macro { + () => { + Location::caller().to_string() + }; +} + +fn main() { + let x = &X(1); + let x_ref = &x; + + let _ = format!("error: something failed at {}", Location::caller()); + let _ = write!( + stdout(), + "error: something failed at {}", + Location::caller() + ); + let _ = writeln!( + stdout(), + "error: something failed at {}", + Location::caller() + ); + print!("error: something failed at {}", Location::caller()); + println!("error: something failed at {}", Location::caller()); + eprint!("error: something failed at {}", Location::caller()); + eprintln!("error: something failed at {}", Location::caller()); + let _ = format_args!("error: something failed at {}", Location::caller()); + assert!(true, "error: something failed at {}", Location::caller()); + assert_eq!(0, 0, "error: something failed at {}", Location::caller()); + assert_ne!(0, 0, "error: something failed at {}", Location::caller()); + panic!("error: something failed at {}", Location::caller()); + println!("{}", *X(1)); + println!("{}", ***Y(&X(1))); + println!("{}", Z(1)); + println!("{}", **x); + println!("{}", ***x_ref); + + println!("error: something failed at {}", Somewhere.to_string()); + println!("{} and again {0}", x.to_string()); + my_macro!(); + println!("error: something failed at {}", my_other_macro!()); +} diff --git a/src/tools/clippy/tests/ui/format_args.rs b/src/tools/clippy/tests/ui/format_args.rs new file mode 100644 index 00000000000..164cc07066d --- /dev/null +++ b/src/tools/clippy/tests/ui/format_args.rs @@ -0,0 +1,105 @@ +// run-rustfix + +#![allow(unreachable_code)] +#![allow(unused_macros)] +#![allow(unused_variables)] +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::eq_op)] +#![warn(clippy::to_string_in_format_args)] + +use std::io::{stdout, Write}; +use std::ops::Deref; +use std::panic::Location; + +struct Somewhere; + +impl ToString for Somewhere { + fn to_string(&self) -> String { + String::from("somewhere") + } +} + +struct X(u32); + +impl Deref for X { + type Target = u32; + + fn deref(&self) -> &u32 { + &self.0 + } +} + +struct Y<'a>(&'a X); + +impl<'a> Deref for Y<'a> { + type Target = &'a X; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +struct Z(u32); + +impl Deref for Z { + type Target = u32; + + fn deref(&self) -> &u32 { + &self.0 + } +} + +impl std::fmt::Display for Z { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Z") + } +} + +macro_rules! my_macro { + () => { + // here be dragons, do not enter (or lint) + println!("error: something failed at {}", Location::caller().to_string()); + }; +} + +macro_rules! my_other_macro { + () => { + Location::caller().to_string() + }; +} + +fn main() { + let x = &X(1); + let x_ref = &x; + + let _ = format!("error: something failed at {}", Location::caller().to_string()); + let _ = write!( + stdout(), + "error: something failed at {}", + Location::caller().to_string() + ); + let _ = writeln!( + stdout(), + "error: something failed at {}", + Location::caller().to_string() + ); + print!("error: something failed at {}", Location::caller().to_string()); + println!("error: something failed at {}", Location::caller().to_string()); + eprint!("error: something failed at {}", Location::caller().to_string()); + eprintln!("error: something failed at {}", Location::caller().to_string()); + let _ = format_args!("error: something failed at {}", Location::caller().to_string()); + assert!(true, "error: something failed at {}", Location::caller().to_string()); + assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string()); + assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string()); + panic!("error: something failed at {}", Location::caller().to_string()); + println!("{}", X(1).to_string()); + println!("{}", Y(&X(1)).to_string()); + println!("{}", Z(1).to_string()); + println!("{}", x.to_string()); + println!("{}", x_ref.to_string()); + + println!("error: something failed at {}", Somewhere.to_string()); + println!("{} and again {0}", x.to_string()); + my_macro!(); + println!("error: something failed at {}", my_other_macro!()); +} diff --git a/src/tools/clippy/tests/ui/format_args.stderr b/src/tools/clippy/tests/ui/format_args.stderr new file mode 100644 index 00000000000..9cfc97edeaf --- /dev/null +++ b/src/tools/clippy/tests/ui/format_args.stderr @@ -0,0 +1,106 @@ +error: `to_string` applied to a type that implements `Display` in `format!` args + --> $DIR/format_args.rs:75:72 + | +LL | let _ = format!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + | + = note: `-D clippy::to-string-in-format-args` implied by `-D warnings` + +error: `to_string` applied to a type that implements `Display` in `write!` args + --> $DIR/format_args.rs:79:27 + | +LL | Location::caller().to_string() + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `writeln!` args + --> $DIR/format_args.rs:84:27 + | +LL | Location::caller().to_string() + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `print!` args + --> $DIR/format_args.rs:86:63 + | +LL | print!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:87:65 + | +LL | println!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `eprint!` args + --> $DIR/format_args.rs:88:64 + | +LL | eprint!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `eprintln!` args + --> $DIR/format_args.rs:89:66 + | +LL | eprintln!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `format_args!` args + --> $DIR/format_args.rs:90:77 + | +LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `assert!` args + --> $DIR/format_args.rs:91:70 + | +LL | assert!(true, "error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `assert_eq!` args + --> $DIR/format_args.rs:92:73 + | +LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `assert_ne!` args + --> $DIR/format_args.rs:93:73 + | +LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `panic!` args + --> $DIR/format_args.rs:94:63 + | +LL | panic!("error: something failed at {}", Location::caller().to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:95:20 + | +LL | println!("{}", X(1).to_string()); + | ^^^^^^^^^^^^^^^^ help: use this: `*X(1)` + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:96:20 + | +LL | println!("{}", Y(&X(1)).to_string()); + | ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))` + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:97:24 + | +LL | println!("{}", Z(1).to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:98:20 + | +LL | println!("{}", x.to_string()); + | ^^^^^^^^^^^^^ help: use this: `**x` + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:99:20 + | +LL | println!("{}", x_ref.to_string()); + | ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref` + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/format_args_unfixable.rs b/src/tools/clippy/tests/ui/format_args_unfixable.rs new file mode 100644 index 00000000000..a8c06c2bde6 --- /dev/null +++ b/src/tools/clippy/tests/ui/format_args_unfixable.rs @@ -0,0 +1,60 @@ +#![allow(clippy::assertions_on_constants)] +#![allow(clippy::eq_op)] +#![warn(clippy::format_in_format_args)] +#![warn(clippy::to_string_in_format_args)] + +use std::io::{stdout, Error, ErrorKind, Write}; +use std::ops::Deref; +use std::panic::Location; + +macro_rules! my_macro { + () => { + // here be dragons, do not enter (or lint) + println!("error: {}", format!("something failed at {}", Location::caller())); + }; +} + +macro_rules! my_other_macro { + () => { + format!("something failed at {}", Location::caller()) + }; +} + +fn main() { + let error = Error::new(ErrorKind::Other, "bad thing"); + let x = 'x'; + + println!("error: {}", format!("something failed at {}", Location::caller())); + println!("{}: {}", error, format!("something failed at {}", Location::caller())); + println!("{:?}: {}", error, format!("something failed at {}", Location::caller())); + println!("{{}}: {}", format!("something failed at {}", Location::caller())); + println!(r#"error: "{}""#, format!("something failed at {}", Location::caller())); + println!("error: {}", format!(r#"something failed at "{}""#, Location::caller())); + println!("error: {}", format!("something failed at {} {0}", Location::caller())); + let _ = format!("error: {}", format!("something failed at {}", Location::caller())); + let _ = write!( + stdout(), + "error: {}", + format!("something failed at {}", Location::caller()) + ); + let _ = writeln!( + stdout(), + "error: {}", + format!("something failed at {}", Location::caller()) + ); + print!("error: {}", format!("something failed at {}", Location::caller())); + eprint!("error: {}", format!("something failed at {}", Location::caller())); + eprintln!("error: {}", format!("something failed at {}", Location::caller())); + let _ = format_args!("error: {}", format!("something failed at {}", Location::caller())); + assert!(true, "error: {}", format!("something failed at {}", Location::caller())); + assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); + assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); + panic!("error: {}", format!("something failed at {}", Location::caller())); + + println!("error: {}", format_args!("something failed at {}", Location::caller())); + println!("error: {:>70}", format!("something failed at {}", Location::caller())); + println!("error: {} {0}", format!("something failed at {}", Location::caller())); + println!("{} and again {0}", format!("hi {}", x)); + my_macro!(); + println!("error: {}", my_other_macro!()); +} diff --git a/src/tools/clippy/tests/ui/format_args_unfixable.stderr b/src/tools/clippy/tests/ui/format_args_unfixable.stderr new file mode 100644 index 00000000000..4476218ad58 --- /dev/null +++ b/src/tools/clippy/tests/ui/format_args_unfixable.stderr @@ -0,0 +1,175 @@ +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:27:5 + | +LL | println!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::format-in-format-args` implied by `-D warnings` + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:28:5 + | +LL | println!("{}: {}", error, format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:29:5 + | +LL | println!("{:?}: {}", error, format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:30:5 + | +LL | println!("{{}}: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:31:5 + | +LL | println!(r#"error: "{}""#, format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:32:5 + | +LL | println!("error: {}", format!(r#"something failed at "{}""#, Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `println!` args + --> $DIR/format_args_unfixable.rs:33:5 + | +LL | println!("error: {}", format!("something failed at {} {0}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `println!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `format!` args + --> $DIR/format_args_unfixable.rs:34:13 + | +LL | let _ = format!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `format!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `write!` args + --> $DIR/format_args_unfixable.rs:35:13 + | +LL | let _ = write!( + | _____________^ +LL | | stdout(), +LL | | "error: {}", +LL | | format!("something failed at {}", Location::caller()) +LL | | ); + | |_____^ + | + = help: combine the `format!(..)` arguments with the outer `write!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `writeln!` args + --> $DIR/format_args_unfixable.rs:40:13 + | +LL | let _ = writeln!( + | _____________^ +LL | | stdout(), +LL | | "error: {}", +LL | | format!("something failed at {}", Location::caller()) +LL | | ); + | |_____^ + | + = help: combine the `format!(..)` arguments with the outer `writeln!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `print!` args + --> $DIR/format_args_unfixable.rs:45:5 + | +LL | print!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `print!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `eprint!` args + --> $DIR/format_args_unfixable.rs:46:5 + | +LL | eprint!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `eprint!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `eprintln!` args + --> $DIR/format_args_unfixable.rs:47:5 + | +LL | eprintln!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `eprintln!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `format_args!` args + --> $DIR/format_args_unfixable.rs:48:13 + | +LL | let _ = format_args!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `format_args!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `assert!` args + --> $DIR/format_args_unfixable.rs:49:5 + | +LL | assert!(true, "error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `assert!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `assert_eq!` args + --> $DIR/format_args_unfixable.rs:50:5 + | +LL | assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `assert_eq!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `assert_ne!` args + --> $DIR/format_args_unfixable.rs:51:5 + | +LL | assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `assert_ne!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: `format!` in `panic!` args + --> $DIR/format_args_unfixable.rs:52:5 + | +LL | panic!("error: {}", format!("something failed at {}", Location::caller())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: combine the `format!(..)` arguments with the outer `panic!(..)` call + = help: or consider changing `format!` to `format_args!` + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed index 859765d08a7..e6f57e9267e 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed @@ -157,4 +157,12 @@ fn main() { if i_64 != 0 { i_64 -= 1; } + + // issue #7831 + // No Lint + if u_32 > 0 { + u_32 -= 1; + } else { + println!("side effect"); + } } diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs index 2f32a7b1578..8bb28d149c6 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs @@ -203,4 +203,12 @@ fn main() { if i_64 != 0 { i_64 -= 1; } + + // issue #7831 + // No Lint + if u_32 > 0 { + u_32 -= 1; + } else { + println!("side effect"); + } } diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr index 366ef36c367..d7cedf9f9f1 100644 --- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr +++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr @@ -110,23 +110,6 @@ LL | | _ => false, LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` -error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_expr_like_matches_macro.rs:166:20 - | -LL | let _res = match &val { - | ____________________^ -LL | | &Some(ref _a) => true, -LL | | _ => false, -LL | | }; - | |_________^ - | - = note: `-D clippy::match-ref-pats` implied by `-D warnings` -help: try - | -LL ~ let _res = match val { -LL ~ Some(ref _a) => true, - | - error: match expression looks like `matches!` macro --> $DIR/match_expr_like_matches_macro.rs:178:20 | @@ -137,21 +120,5 @@ LL | | _ => false, LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` -error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_expr_like_matches_macro.rs:178:20 - | -LL | let _res = match &val { - | ____________________^ -LL | | &Some(ref _a) => true, -LL | | _ => false, -LL | | }; - | |_________^ - | -help: try - | -LL ~ let _res = match val { -LL ~ Some(ref _a) => true, - | - -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.rs b/src/tools/clippy/tests/ui/match_overlapping_arm.rs index 846d665d1d8..ff91c4498ec 100644 --- a/src/tools/clippy/tests/ui/match_overlapping_arm.rs +++ b/src/tools/clippy/tests/ui/match_overlapping_arm.rs @@ -10,98 +10,95 @@ fn overlapping() { const FOO: u64 = 2; match 42 { - 0..=10 => println!("0 ... 10"), - 0..=11 => println!("0 ... 11"), + 0..=10 => println!("0..=10"), + 0..=11 => println!("0..=11"), _ => (), } match 42 { - 0..=5 => println!("0 ... 5"), - 6..=7 => println!("6 ... 7"), - FOO..=11 => println!("0 ... 11"), + 0..=5 => println!("0..=5"), + 6..=7 => println!("6..=7"), + FOO..=11 => println!("FOO..=11"), _ => (), } match 42 { 2 => println!("2"), - 0..=5 => println!("0 ... 5"), + 0..=5 => println!("0..=5"), _ => (), } match 42 { 2 => println!("2"), - 0..=2 => println!("0 ... 2"), + 0..=2 => println!("0..=2"), _ => (), } match 42 { - 0..=10 => println!("0 ... 10"), - 11..=50 => println!("11 ... 50"), + 0..=10 => println!("0..=10"), + 11..=50 => println!("11..=50"), _ => (), } match 42 { 2 => println!("2"), - 0..2 => println!("0 .. 2"), + 0..2 => println!("0..2"), _ => (), } match 42 { - 0..10 => println!("0 .. 10"), - 10..50 => println!("10 .. 50"), + 0..10 => println!("0..10"), + 10..50 => println!("10..50"), _ => (), } match 42 { - 0..11 => println!("0 .. 11"), - 0..=11 => println!("0 ... 11"), + 0..11 => println!("0..11"), + 0..=11 => println!("0..=11"), _ => (), } match 42 { - 5..7 => println!("5 .. 7"), - 0..10 => println!("0 .. 10"), + 5..7 => println!("5..7"), + 0..10 => println!("0..10"), _ => (), } match 42 { - 5..10 => println!("5 .. 10"), - 0..=10 => println!("0 ... 10"), + 5..10 => println!("5..10"), + 0..=10 => println!("0..=10"), _ => (), } match 42 { - 0..14 => println!("0 .. 14"), - 5..10 => println!("5 .. 10"), + 0..14 => println!("0..14"), + 5..10 => println!("5..10"), _ => (), } match 42 { - 5..14 => println!("5 .. 14"), - 0..=10 => println!("0 ... 10"), + 5..14 => println!("5..14"), + 0..=10 => println!("0..=10"), _ => (), } match 42 { - 0..7 => println!("0 .. 7"), - 0..=10 => println!("0 ... 10"), + 0..7 => println!("0..7"), + 0..=10 => println!("0..=10"), _ => (), } - /* - // FIXME(JohnTitor): uncomment this once rustfmt knows half-open patterns match 42 { - 0.. => println!("0 .. 42"), - 3.. => println!("3 .. 42"), + 3.. => println!("3.."), + 0.. => println!("0.."), _ => (), } match 42 { - ..=23 => println!("0 ... 23"), - ..26 => println!("0 .. 26"), + ..=23 => println!("..=23"), + ..26 => println!("..26"), _ => (), } - */ if let None = Some(42) { // nothing diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr index 359fa49f51b..c2b3f173c2b 100644 --- a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr +++ b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr @@ -1,63 +1,75 @@ error: some ranges overlap --> $DIR/match_overlapping_arm.rs:13:9 | -LL | 0..=10 => println!("0 ... 10"), +LL | 0..=10 => println!("0..=10"), | ^^^^^^ | = note: `-D clippy::match-overlapping-arm` implied by `-D warnings` note: overlaps with this --> $DIR/match_overlapping_arm.rs:14:9 | -LL | 0..=11 => println!("0 ... 11"), +LL | 0..=11 => println!("0..=11"), | ^^^^^^ error: some ranges overlap --> $DIR/match_overlapping_arm.rs:19:9 | -LL | 0..=5 => println!("0 ... 5"), +LL | 0..=5 => println!("0..=5"), | ^^^^^ | note: overlaps with this --> $DIR/match_overlapping_arm.rs:21:9 | -LL | FOO..=11 => println!("0 ... 11"), +LL | FOO..=11 => println!("FOO..=11"), | ^^^^^^^^ error: some ranges overlap --> $DIR/match_overlapping_arm.rs:56:9 | -LL | 0..11 => println!("0 .. 11"), +LL | 0..11 => println!("0..11"), | ^^^^^ | note: overlaps with this --> $DIR/match_overlapping_arm.rs:57:9 | -LL | 0..=11 => println!("0 ... 11"), +LL | 0..=11 => println!("0..=11"), | ^^^^^^ error: some ranges overlap --> $DIR/match_overlapping_arm.rs:81:9 | -LL | 0..=10 => println!("0 ... 10"), +LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this --> $DIR/match_overlapping_arm.rs:80:9 | -LL | 5..14 => println!("5 .. 14"), +LL | 5..14 => println!("5..14"), | ^^^^^ error: some ranges overlap --> $DIR/match_overlapping_arm.rs:86:9 | -LL | 0..7 => println!("0 .. 7"), +LL | 0..7 => println!("0..7"), | ^^^^ | note: overlaps with this --> $DIR/match_overlapping_arm.rs:87:9 | -LL | 0..=10 => println!("0 ... 10"), +LL | 0..=10 => println!("0..=10"), | ^^^^^^ -error: aborting due to 5 previous errors +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:98:9 + | +LL | ..=23 => println!("..=23"), + | ^^^^^ + | +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:99:9 + | +LL | ..26 => println!("..26"), + | ^^^^ + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/match_ref_pats.rs b/src/tools/clippy/tests/ui/match_ref_pats.rs index 6cbb4d32b0d..50246486bb6 100644 --- a/src/tools/clippy/tests/ui/match_ref_pats.rs +++ b/src/tools/clippy/tests/ui/match_ref_pats.rs @@ -72,4 +72,46 @@ mod ice_3719 { } } +mod issue_7740 { + macro_rules! foobar_variant( + ($idx:expr) => (FooBar::get($idx).unwrap()) + ); + + enum FooBar { + Foo, + Bar, + FooBar, + BarFoo, + } + + impl FooBar { + fn get(idx: u8) -> Option<&'static Self> { + match idx { + 0 => Some(&FooBar::Foo), + 1 => Some(&FooBar::Bar), + 2 => Some(&FooBar::FooBar), + 3 => Some(&FooBar::BarFoo), + _ => None, + } + } + } + + fn issue_7740() { + // Issue #7740 + match foobar_variant!(0) { + &FooBar::Foo => println!("Foo"), + &FooBar::Bar => println!("Bar"), + &FooBar::FooBar => println!("FooBar"), + _ => println!("Wild"), + } + + // This shouldn't trigger + if let &FooBar::BarFoo = foobar_variant!(3) { + println!("BarFoo"); + } else { + println!("Wild"); + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/match_ref_pats.stderr b/src/tools/clippy/tests/ui/match_ref_pats.stderr index 072aff445e9..901820077e2 100644 --- a/src/tools/clippy/tests/ui/match_ref_pats.stderr +++ b/src/tools/clippy/tests/ui/match_ref_pats.stderr @@ -15,21 +15,6 @@ LL ~ Some(v) => println!("{:?}", v), LL ~ None => println!("none"), | -error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:18:5 - | -LL | / match tup { -LL | | &(v, 1) => println!("{}", v), -LL | | _ => println!("none"), -LL | | } - | |_____^ - | -help: instead of prefixing all patterns with `&`, you can dereference the expression - | -LL ~ match *tup { -LL ~ (v, 1) => println!("{}", v), - | - error: you don't need to add `&` to both the expression and the patterns --> $DIR/match_ref_pats.rs:24:5 | @@ -54,52 +39,30 @@ LL | if let &None = a { | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` -error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:36:5 - | -LL | / if let &None = a { -LL | | println!("none"); -LL | | } - | |_____^ - | -help: instead of prefixing all patterns with `&`, you can dereference the expression - | -LL | if let None = *a { - | ~~~~ ~~ - error: redundant pattern matching, consider using `is_none()` --> $DIR/match_ref_pats.rs:41:12 | LL | if let &None = &b { | -------^^^^^----- help: try this: `if b.is_none()` -error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_ref_pats.rs:41:5 - | -LL | / if let &None = &b { -LL | | println!("none"); -LL | | } - | |_____^ - | -help: try - | -LL | if let None = b { - | ~~~~ ~ - error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:68:9 + --> $DIR/match_ref_pats.rs:101:9 | -LL | / match foo_variant!(0) { -LL | | &Foo::A => println!("A"), +LL | / match foobar_variant!(0) { +LL | | &FooBar::Foo => println!("Foo"), +LL | | &FooBar::Bar => println!("Bar"), +LL | | &FooBar::FooBar => println!("FooBar"), LL | | _ => println!("Wild"), LL | | } | |_________^ | help: instead of prefixing all patterns with `&`, you can dereference the expression | -LL ~ match *foo_variant!(0) { -LL ~ Foo::A => println!("A"), +LL ~ match *foobar_variant!(0) { +LL ~ FooBar::Foo => println!("Foo"), +LL ~ FooBar::Bar => println!("Bar"), +LL ~ FooBar::FooBar => println!("FooBar"), | -error: aborting due to 8 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/match_str_case_mismatch.rs b/src/tools/clippy/tests/ui/match_str_case_mismatch.rs new file mode 100644 index 00000000000..208a4bba3d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_str_case_mismatch.rs @@ -0,0 +1,98 @@ +#![warn(clippy::match_str_case_mismatch)] + +// Valid + +fn as_str_match() { + let var = "BAR"; + + match var.to_ascii_lowercase().as_str() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn addrof_unary_match() { + let var = "BAR"; + + match &*var.to_ascii_lowercase() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn alternating_chain() { + let var = "BAR"; + + match &*var + .to_ascii_lowercase() + .to_uppercase() + .to_lowercase() + .to_ascii_uppercase() + { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +fn unrelated_method() { + struct Item { + a: String, + } + + impl Item { + #[allow(clippy::wrong_self_convention)] + fn to_lowercase(self) -> String { + self.a + } + } + + let item = Item { a: String::from("BAR") }; + + match &*item.to_lowercase() { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +// Invalid + +fn as_str_match_mismatch() { + let var = "BAR"; + + match var.to_ascii_lowercase().as_str() { + "foo" => {}, + "Bar" => {}, + _ => {}, + } +} + +fn addrof_unary_match_mismatch() { + let var = "BAR"; + + match &*var.to_ascii_lowercase() { + "foo" => {}, + "Bar" => {}, + _ => {}, + } +} + +fn alternating_chain_mismatch() { + let var = "BAR"; + + match &*var + .to_ascii_lowercase() + .to_uppercase() + .to_lowercase() + .to_ascii_uppercase() + { + "FOO" => {}, + "bAR" => {}, + _ => {}, + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_str_case_mismatch.stderr b/src/tools/clippy/tests/ui/match_str_case_mismatch.stderr new file mode 100644 index 00000000000..fa023477a9c --- /dev/null +++ b/src/tools/clippy/tests/ui/match_str_case_mismatch.stderr @@ -0,0 +1,36 @@ +error: this `match` arm has a differing case than its expression + --> $DIR/match_str_case_mismatch.rs:68:9 + | +LL | "Bar" => {}, + | ^^^^^ + | + = note: `-D clippy::match-str-case-mismatch` implied by `-D warnings` +help: consider changing the case of this arm to respect `to_ascii_lowercase` + | +LL | "bar" => {}, + | ~~~~~ + +error: this `match` arm has a differing case than its expression + --> $DIR/match_str_case_mismatch.rs:78:9 + | +LL | "Bar" => {}, + | ^^^^^ + | +help: consider changing the case of this arm to respect `to_ascii_lowercase` + | +LL | "bar" => {}, + | ~~~~~ + +error: this `match` arm has a differing case than its expression + --> $DIR/match_str_case_mismatch.rs:93:9 + | +LL | "bAR" => {}, + | ^^^^^ + | +help: consider changing the case of this arm to respect `to_ascii_uppercase` + | +LL | "BAR" => {}, + | ~~~~~ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_mut.rs b/src/tools/clippy/tests/ui/mut_mut.rs index 8965cef66de..be854d94183 100644 --- a/src/tools/clippy/tests/ui/mut_mut.rs +++ b/src/tools/clippy/tests/ui/mut_mut.rs @@ -1,6 +1,11 @@ +// aux-build:macro_rules.rs + #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::mut_mut)] +#[macro_use] +extern crate macro_rules; + fn fun(x: &mut &mut u32) -> bool { **x > 0 } @@ -47,3 +52,8 @@ fn issue939() { println!(":{}", arg); } } + +fn issue6922() { + // do not lint from an external macro + mut_mut!(); +} diff --git a/src/tools/clippy/tests/ui/mut_mut.stderr b/src/tools/clippy/tests/ui/mut_mut.stderr index 0fed6953cb8..6820a85aa54 100644 --- a/src/tools/clippy/tests/ui/mut_mut.stderr +++ b/src/tools/clippy/tests/ui/mut_mut.stderr @@ -1,5 +1,5 @@ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:4:11 + --> $DIR/mut_mut.rs:9:11 | LL | fn fun(x: &mut &mut u32) -> bool { | ^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | fn fun(x: &mut &mut u32) -> bool { = note: `-D clippy::mut-mut` implied by `-D warnings` error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:20:17 + --> $DIR/mut_mut.rs:25:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:14:9 + --> $DIR/mut_mut.rs:19:9 | LL | &mut $p | ^^^^^^^ @@ -24,37 +24,37 @@ LL | let mut z = mut_ptr!(&mut 3u32); = note: this error originates in the macro `mut_ptr` (in Nightly builds, run with -Z macro-backtrace for more info) error: this expression mutably borrows a mutable reference. Consider reborrowing - --> $DIR/mut_mut.rs:22:21 + --> $DIR/mut_mut.rs:27:21 | LL | let mut y = &mut x; | ^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:26:32 + --> $DIR/mut_mut.rs:31:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:26:16 + --> $DIR/mut_mut.rs:31:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:31:37 + --> $DIR/mut_mut.rs:36:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:31:16 + --> $DIR/mut_mut.rs:36:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> $DIR/mut_mut.rs:31:21 + --> $DIR/mut_mut.rs:36:21 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs index 7ec845adfaa..7bcc4cad0d3 100644 --- a/src/tools/clippy/tests/ui/no_effect.rs +++ b/src/tools/clippy/tests/ui/no_effect.rs @@ -1,5 +1,5 @@ #![feature(box_syntax)] -#![warn(clippy::no_effect)] +#![warn(clippy::no_effect_underscore_binding)] #![allow(dead_code)] #![allow(path_statements)] #![allow(clippy::deref_addrof)] @@ -90,6 +90,10 @@ fn main() { || x += 5; let s: String = "foo".into(); FooString { s: s }; + let _unused = 1; + let _penguin = || println!("Some helpful closure"); + let _duck = Struct { field: 0 }; + let _cat = [2, 4, 6, 8][2]; #[allow(clippy::no_effect)] 0; @@ -97,6 +101,8 @@ fn main() { // Do not warn get_number(); unsafe { unsafe_fn() }; + let _used = get_struct(); + let _x = vec![1]; DropUnit; DropStruct { field: 0 }; DropTuple(0); diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr index 6b24675ac2d..a5dbc9fef45 100644 --- a/src/tools/clippy/tests/ui/no_effect.stderr +++ b/src/tools/clippy/tests/ui/no_effect.stderr @@ -156,5 +156,31 @@ error: statement with no effect LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 26 previous errors +error: binding to `_` prefixed variable with no side-effect + --> $DIR/no_effect.rs:93: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 + | +LL | let _penguin = || println!("Some helpful closure"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: binding to `_` prefixed variable with no side-effect + --> $DIR/no_effect.rs:95:5 + | +LL | let _duck = Struct { field: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: binding to `_` prefixed variable with no side-effect + --> $DIR/no_effect.rs:96:5 + | +LL | let _cat = [2, 4, 6, 8][2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 30 previous errors diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed index a3ebe5d0703..4077f1920a3 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.fixed +++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed @@ -1,8 +1,7 @@ // edition:2018 // run-rustfix #![warn(clippy::option_if_let_else)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::ref_option_ref, clippy::equatable_if_let)] +#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)] fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs index b11df3db60f..2f414e129d5 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.rs +++ b/src/tools/clippy/tests/ui/option_if_let_else.rs @@ -1,8 +1,7 @@ // edition:2018 // run-rustfix #![warn(clippy::option_if_let_else)] -#![allow(clippy::redundant_closure)] -#![allow(clippy::ref_option_ref, clippy::equatable_if_let)] +#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)] fn bad1(string: Option<&str>) -> (bool, &str) { if let Some(x) = string { diff --git a/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr index ed748ee8b39..803d941c36d 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.stderr +++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:8:5 + --> $DIR/option_if_let_else.rs:7:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -11,19 +11,19 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:26:13 + --> $DIR/option_if_let_else.rs:25:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:27:13 + --> $DIR/option_if_let_else.rs:26:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:28:13 + --> $DIR/option_if_let_else.rs:27:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -43,13 +43,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:34:13 + --> $DIR/option_if_let_else.rs:33:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:35:13 + --> $DIR/option_if_let_else.rs:34:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -69,7 +69,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:41:13 + --> $DIR/option_if_let_else.rs:40:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -89,7 +89,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:50:5 + --> $DIR/option_if_let_else.rs:49:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -108,7 +108,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:63:13 + --> $DIR/option_if_let_else.rs:62:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -120,7 +120,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:72:13 + --> $DIR/option_if_let_else.rs:71:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -143,13 +143,13 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:101:13 + --> $DIR/option_if_let_else.rs:100: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:110:13 + --> $DIR/option_if_let_else.rs:109:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -171,13 +171,13 @@ LL ~ }); | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:138:13 + --> $DIR/option_if_let_else.rs:137: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)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:142:13 + --> $DIR/option_if_let_else.rs:141:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed index 0b5746cb522..ccb2e5a302e 100644 --- a/src/tools/clippy/tests/ui/question_mark.fixed +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -104,6 +104,21 @@ fn func() -> Option<i32> { Some(0) } +fn result_func(x: Result<i32, &str>) -> Result<i32, &str> { + let _ = x?; + + x?; + + // No warning + let y = if let Ok(x) = x { + x + } else { + return Err("some error"); + }; + + Ok(y) +} + fn main() { some_func(Some(42)); some_func(None); @@ -123,4 +138,6 @@ fn main() { returns_something_similar_to_option(so); func(); + + let _ = result_func(Ok(42)); } diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs index 0f0825c9334..ca3722371f5 100644 --- a/src/tools/clippy/tests/ui/question_mark.rs +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -134,6 +134,23 @@ fn func() -> Option<i32> { Some(0) } +fn result_func(x: Result<i32, &str>) -> Result<i32, &str> { + let _ = if let Ok(x) = x { x } else { return x }; + + if x.is_err() { + return x; + } + + // No warning + let y = if let Ok(x) = x { + x + } else { + return Err("some error"); + }; + + Ok(y) +} + fn main() { some_func(Some(42)); some_func(None); @@ -153,4 +170,6 @@ fn main() { returns_something_similar_to_option(so); func(); + + let _ = result_func(Ok(42)); } diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr index 6f330cfa385..161588cb73c 100644 --- a/src/tools/clippy/tests/ui/question_mark.stderr +++ b/src/tools/clippy/tests/ui/question_mark.stderr @@ -100,5 +100,19 @@ LL | | return None; LL | | } | |_____^ help: replace it with: `f()?;` -error: aborting due to 11 previous errors +error: this if-let-else may be rewritten with the `?` operator + --> $DIR/question_mark.rs:138:13 + | +LL | let _ = if let Ok(x) = x { x } else { return x }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:140:5 + | +LL | / if x.is_err() { +LL | | return x; +LL | | } + | |_____^ help: replace it with: `x?;` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs index 9644a232968..7a45f1b18d4 100644 --- a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs +++ b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs @@ -98,3 +98,15 @@ fn unsafe_checks() { let mut s = MaybeUninit::<String>::uninit(); let _d = || unsafe { ptr::drop_in_place(s.as_mut_ptr()) }; } + +// Issue #7768 +#[rustfmt::skip] +fn macro_with_semicolon() { + macro_rules! repro { + () => { + while false { + } + }; + } + repro!(); +} diff --git a/src/tools/clippy/tests/ui/shadow.rs b/src/tools/clippy/tests/ui/shadow.rs index 02e838456d0..55caef59f7f 100644 --- a/src/tools/clippy/tests/ui/shadow.rs +++ b/src/tools/clippy/tests/ui/shadow.rs @@ -17,6 +17,11 @@ fn shadow_reuse() -> Option<()> { let x = foo(x); let x = || x; let x = Some(1).map(|_| x)?; + let y = 1; + let y = match y { + 1 => 2, + _ => 3, + }; None } diff --git a/src/tools/clippy/tests/ui/shadow.stderr b/src/tools/clippy/tests/ui/shadow.stderr index 8b60e072c93..feed6e1ba8b 100644 --- a/src/tools/clippy/tests/ui/shadow.stderr +++ b/src/tools/clippy/tests/ui/shadow.stderr @@ -47,7 +47,7 @@ note: previous binding is here LL | let x = &mut x; | ^ -error: `x` is shadowed by `x.0` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:13:9 | LL | let x = x.0; @@ -60,7 +60,7 @@ note: previous binding is here LL | let x = ([[0]], ()); | ^ -error: `x` is shadowed by `x[0]` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:14:9 | LL | let x = x[0]; @@ -72,7 +72,7 @@ note: previous binding is here LL | let x = x.0; | ^ -error: `x` is shadowed by `x` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:15:10 | LL | let [x] = x; @@ -84,7 +84,7 @@ note: previous binding is here LL | let x = x[0]; | ^ -error: `x` is shadowed by `Some(x)` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:16:9 | LL | let x = Some(x); @@ -96,7 +96,7 @@ note: previous binding is here LL | let [x] = x; | ^ -error: `x` is shadowed by `foo(x)` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:17:9 | LL | let x = foo(x); @@ -108,7 +108,7 @@ note: previous binding is here LL | let x = Some(x); | ^ -error: `x` is shadowed by `|| x` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:18:9 | LL | let x = || x; @@ -120,7 +120,7 @@ note: previous binding is here LL | let x = foo(x); | ^ -error: `x` is shadowed by `Some(1).map(|_| x)?` which reuses the original value +error: `x` is shadowed --> $DIR/shadow.rs:19:9 | LL | let x = Some(1).map(|_| x)?; @@ -132,102 +132,114 @@ note: previous binding is here LL | let x = || x; | ^ +error: `y` is shadowed + --> $DIR/shadow.rs:21:9 + | +LL | let y = match y { + | ^ + | +note: previous binding is here + --> $DIR/shadow.rs:20:9 + | +LL | let y = 1; + | ^ + error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:25:9 + --> $DIR/shadow.rs:30:9 | LL | let x = 2; | ^ | = note: `-D clippy::shadow-unrelated` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:24:9 + --> $DIR/shadow.rs:29:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:30:13 + --> $DIR/shadow.rs:35:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:29:10 + --> $DIR/shadow.rs:34:10 | LL | fn f(x: u32) { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:35:14 + --> $DIR/shadow.rs:40:14 | LL | Some(x) => { | ^ | note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:37:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:36:17 + --> $DIR/shadow.rs:41:17 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:35:14 + --> $DIR/shadow.rs:40:14 | LL | Some(x) => { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:40:17 + --> $DIR/shadow.rs:45:17 | LL | if let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:37:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:41:20 + --> $DIR/shadow.rs:46:20 | LL | while let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:37:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:42:15 + --> $DIR/shadow.rs:47:15 | LL | let _ = |[x]: [u32; 1]| { | ^ | note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:37:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:43:13 + --> $DIR/shadow.rs:48:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:42:15 + --> $DIR/shadow.rs:47:15 | LL | let _ = |[x]: [u32; 1]| { | ^ -error: aborting due to 19 previous errors +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/to_string_in_display.rs b/src/tools/clippy/tests/ui/to_string_in_display.rs index eb8105c6b6d..3ccdcd1117b 100644 --- a/src/tools/clippy/tests/ui/to_string_in_display.rs +++ b/src/tools/clippy/tests/ui/to_string_in_display.rs @@ -1,5 +1,5 @@ #![warn(clippy::to_string_in_display)] -#![allow(clippy::inherent_to_string_shadow_display)] +#![allow(clippy::inherent_to_string_shadow_display, clippy::to_string_in_format_args)] use std::fmt; diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.rs b/src/tools/clippy/tests/ui/trailing_empty_array.rs new file mode 100644 index 00000000000..501c9eb7651 --- /dev/null +++ b/src/tools/clippy/tests/ui/trailing_empty_array.rs @@ -0,0 +1,186 @@ +#![warn(clippy::trailing_empty_array)] +#![feature(const_generics_defaults)] + +// Do lint: + +struct RarelyUseful { + field: i32, + last: [usize; 0], +} + +struct OnlyField { + first_and_last: [usize; 0], +} + +struct GenericArrayType<T> { + field: i32, + last: [T; 0], +} + +#[must_use] +struct OnlyAnotherAttribute { + field: i32, + last: [usize; 0], +} + +#[derive(Debug)] +struct OnlyADeriveAttribute { + field: i32, + last: [usize; 0], +} + +const ZERO: usize = 0; +struct ZeroSizedWithConst { + field: i32, + last: [usize; ZERO], +} + +#[allow(clippy::eq_op)] +const fn compute_zero() -> usize { + (4 + 6) - (2 * 5) +} +struct ZeroSizedWithConstFunction { + field: i32, + last: [usize; compute_zero()], +} + +const fn compute_zero_from_arg(x: usize) -> usize { + x - 1 +} +struct ZeroSizedWithConstFunction2 { + field: i32, + last: [usize; compute_zero_from_arg(1)], +} + +struct ZeroSizedArrayWrapper([usize; 0]); + +struct TupleStruct(i32, [usize; 0]); + +struct LotsOfFields { + f1: u32, + f2: u32, + f3: u32, + f4: u32, + f5: u32, + f6: u32, + f7: u32, + f8: u32, + f9: u32, + f10: u32, + f11: u32, + f12: u32, + f13: u32, + f14: u32, + f15: u32, + f16: u32, + last: [usize; 0], +} + +// Don't lint + +#[repr(C)] +struct GoodReason { + field: i32, + last: [usize; 0], +} + +#[repr(C)] +struct OnlyFieldWithReprC { + first_and_last: [usize; 0], +} + +struct NonZeroSizedArray { + field: i32, + last: [usize; 1], +} + +struct NotLastField { + f1: u32, + zero_sized: [usize; 0], + last: i32, +} + +const ONE: usize = 1; +struct NonZeroSizedWithConst { + field: i32, + last: [usize; ONE], +} + +#[derive(Debug)] +#[repr(C)] +struct AlsoADeriveAttribute { + field: i32, + last: [usize; 0], +} + +#[must_use] +#[repr(C)] +struct AlsoAnotherAttribute { + field: i32, + last: [usize; 0], +} + +#[repr(packed)] +struct ReprPacked { + field: i32, + last: [usize; 0], +} + +#[repr(C, packed)] +struct ReprCPacked { + field: i32, + last: [usize; 0], +} + +#[repr(align(64))] +struct ReprAlign { + field: i32, + last: [usize; 0], +} +#[repr(C, align(64))] +struct ReprCAlign { + field: i32, + last: [usize; 0], +} + +// NOTE: because of https://doc.rust-lang.org/stable/reference/type-layout.html#primitive-representation-of-enums-with-fields and I'm not sure when in the compilation pipeline that would happen +#[repr(C)] +enum DontLintAnonymousStructsFromDesuraging { + A(u32), + B(f32, [u64; 0]), + C { x: u32, y: [u64; 0] }, +} + +#[repr(C)] +struct TupleStructReprC(i32, [usize; 0]); + +type NamedTuple = (i32, [usize; 0]); + +#[rustfmt::skip] // [rustfmt#4995](https://github.com/rust-lang/rustfmt/issues/4995) +struct ConstParamZeroDefault<const N: usize = 0> { + field: i32, + last: [usize; N], +} + +struct ConstParamNoDefault<const N: usize> { + field: i32, + last: [usize; N], +} + +#[rustfmt::skip] +struct ConstParamNonZeroDefault<const N: usize = 1> { + field: i32, + last: [usize; N], +} + +struct TwoGenericParams<T, const N: usize> { + field: i32, + last: [T; N], +} + +type A = ConstParamZeroDefault; +type B = ConstParamZeroDefault<0>; +type C = ConstParamNoDefault<0>; +type D = ConstParamNonZeroDefault<0>; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.stderr b/src/tools/clippy/tests/ui/trailing_empty_array.stderr new file mode 100644 index 00000000000..d88aa0504b5 --- /dev/null +++ b/src/tools/clippy/tests/ui/trailing_empty_array.stderr @@ -0,0 +1,120 @@ +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_empty_array.rs:6:1 + | +LL | / struct RarelyUseful { +LL | | field: i32, +LL | | last: [usize; 0], +LL | | } + | |_^ + | + = note: `-D clippy::trailing-empty-array` implied by `-D warnings` + = help: consider annotating `RarelyUseful` with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_empty_array.rs:11:1 + | +LL | / struct OnlyField { +LL | | first_and_last: [usize; 0], +LL | | } + | |_^ + | + = help: consider annotating `OnlyField` with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_empty_array.rs:15:1 + | +LL | / struct GenericArrayType<T> { +LL | | field: i32, +LL | | last: [T; 0], +LL | | } + | |_^ + | + = help: consider annotating `GenericArrayType` with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_empty_array.rs:21:1 + | +LL | / struct OnlyAnotherAttribute { +LL | | field: i32, +LL | | last: [usize; 0], +LL | | } + | |_^ + | + = help: consider annotating `OnlyAnotherAttribute` with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_empty_array.rs:27:1 + | +LL | / struct OnlyADeriveAttribute { +LL | | field: i32, +LL | | last: [usize; 0], +LL | | } + | |_^ + | + = help: consider annotating `OnlyADeriveAttribute` with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_empty_array.rs:33:1 + | +LL | / struct ZeroSizedWithConst { +LL | | field: i32, +LL | | last: [usize; ZERO], +LL | | } + | |_^ + | + = help: consider annotating `ZeroSizedWithConst` with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_empty_array.rs:42:1 + | +LL | / struct ZeroSizedWithConstFunction { +LL | | field: i32, +LL | | last: [usize; compute_zero()], +LL | | } + | |_^ + | + = help: consider annotating `ZeroSizedWithConstFunction` with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_empty_array.rs:50:1 + | +LL | / struct ZeroSizedWithConstFunction2 { +LL | | field: i32, +LL | | last: [usize; compute_zero_from_arg(1)], +LL | | } + | |_^ + | + = help: consider annotating `ZeroSizedWithConstFunction2` with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_empty_array.rs:55:1 + | +LL | struct ZeroSizedArrayWrapper([usize; 0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider annotating `ZeroSizedArrayWrapper` with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_empty_array.rs:57:1 + | +LL | struct TupleStruct(i32, [usize; 0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider annotating `TupleStruct` with `#[repr(C)]` or another `repr` attribute + +error: trailing zero-sized array in a struct which is not marked with a `repr` attribute + --> $DIR/trailing_empty_array.rs:59:1 + | +LL | / struct LotsOfFields { +LL | | f1: u32, +LL | | f2: u32, +LL | | f3: u32, +... | +LL | | last: [usize; 0], +LL | | } + | |_^ + | + = help: consider annotating `LotsOfFields` with `#[repr(C)]` or another `repr` attribute + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index bce4c81b78a..6a7037d8f38 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -103,6 +103,33 @@ mod int_to_float { } } +mod num_to_bytes { + fn test() { + unsafe { + let _: [u8; 1] = std::mem::transmute(0u8); + let _: [u8; 4] = std::mem::transmute(0u32); + let _: [u8; 16] = std::mem::transmute(0u128); + let _: [u8; 1] = std::mem::transmute(0i8); + let _: [u8; 4] = std::mem::transmute(0i32); + let _: [u8; 16] = std::mem::transmute(0i128); + let _: [u8; 4] = std::mem::transmute(0.0f32); + let _: [u8; 8] = std::mem::transmute(0.0f64); + } + } + const fn test_const() { + unsafe { + let _: [u8; 1] = std::mem::transmute(0u8); + let _: [u8; 4] = std::mem::transmute(0u32); + let _: [u8; 16] = std::mem::transmute(0u128); + let _: [u8; 1] = std::mem::transmute(0i8); + let _: [u8; 4] = std::mem::transmute(0i32); + let _: [u8; 16] = std::mem::transmute(0i128); + let _: [u8; 4] = std::mem::transmute(0.0f32); + let _: [u8; 8] = std::mem::transmute(0.0f64); + } + } +} + fn bytes_to_str(b: &[u8], mb: &mut [u8]) { let _: &str = unsafe { std::mem::transmute(b) }; let _: &mut str = unsafe { std::mem::transmute(mb) }; diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr index e31accb982a..86537153e32 100644 --- a/src/tools/clippy/tests/ui/transmute.stderr +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -140,8 +140,94 @@ error: transmute from a `i64` to a `f64` LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` +error: transmute from a `u8` to a `[u8; 1]` + --> $DIR/transmute.rs:109:30 + | +LL | let _: [u8; 1] = std::mem::transmute(0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` + | + = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings` + +error: transmute from a `u32` to a `[u8; 4]` + --> $DIR/transmute.rs:110:30 + | +LL | let _: [u8; 4] = std::mem::transmute(0u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` + +error: transmute from a `u128` to a `[u8; 16]` + --> $DIR/transmute.rs:111:31 + | +LL | let _: [u8; 16] = std::mem::transmute(0u128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` + +error: transmute from a `i8` to a `[u8; 1]` + --> $DIR/transmute.rs:112:30 + | +LL | let _: [u8; 1] = std::mem::transmute(0i8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` + +error: transmute from a `i32` to a `[u8; 4]` + --> $DIR/transmute.rs:113:30 + | +LL | let _: [u8; 4] = std::mem::transmute(0i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` + +error: transmute from a `i128` to a `[u8; 16]` + --> $DIR/transmute.rs:114:31 + | +LL | let _: [u8; 16] = std::mem::transmute(0i128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` + +error: transmute from a `f32` to a `[u8; 4]` + --> $DIR/transmute.rs:115:30 + | +LL | let _: [u8; 4] = std::mem::transmute(0.0f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` + +error: transmute from a `f64` to a `[u8; 8]` + --> $DIR/transmute.rs:116:30 + | +LL | let _: [u8; 8] = std::mem::transmute(0.0f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` + +error: transmute from a `u8` to a `[u8; 1]` + --> $DIR/transmute.rs:121:30 + | +LL | let _: [u8; 1] = std::mem::transmute(0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` + +error: transmute from a `u32` to a `[u8; 4]` + --> $DIR/transmute.rs:122:30 + | +LL | let _: [u8; 4] = std::mem::transmute(0u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` + +error: transmute from a `u128` to a `[u8; 16]` + --> $DIR/transmute.rs:123:31 + | +LL | let _: [u8; 16] = std::mem::transmute(0u128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` + +error: transmute from a `i8` to a `[u8; 1]` + --> $DIR/transmute.rs:124:30 + | +LL | let _: [u8; 1] = std::mem::transmute(0i8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` + +error: transmute from a `i32` to a `[u8; 4]` + --> $DIR/transmute.rs:125:30 + | +LL | let _: [u8; 4] = std::mem::transmute(0i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` + +error: transmute from a `i128` to a `[u8; 16]` + --> $DIR/transmute.rs:126:31 + | +LL | let _: [u8; 16] = std::mem::transmute(0i128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` + error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:107:28 + --> $DIR/transmute.rs:134:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -149,10 +235,10 @@ LL | let _: &str = unsafe { std::mem::transmute(b) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:108:32 + --> $DIR/transmute.rs:135:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 24 previous errors +error: aborting due to 38 previous errors diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs new file mode 100644 index 00000000000..52577323a58 --- /dev/null +++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs @@ -0,0 +1,287 @@ +#![warn(clippy::undocumented_unsafe_blocks)] + +// Valid comments + +fn nested_local() { + let _ = { + let _ = { + // Safety: + let _ = unsafe {}; + }; + }; +} + +fn deep_nest() { + let _ = { + let _ = { + // Safety: + let _ = unsafe {}; + + // Safety: + unsafe {}; + + let _ = { + let _ = { + let _ = { + let _ = { + let _ = { + // Safety: + let _ = unsafe {}; + + // Safety: + unsafe {}; + }; + }; + }; + + // Safety: + unsafe {}; + }; + }; + }; + + // Safety: + unsafe {}; + }; + + // Safety: + unsafe {}; +} + +fn local_tuple_expression() { + // Safety: + let _ = (42, unsafe {}); +} + +fn line_comment() { + // Safety: + unsafe {} +} + +fn line_comment_newlines() { + // Safety: + + unsafe {} +} + +fn line_comment_empty() { + // Safety: + // + // + // + unsafe {} +} + +fn line_comment_with_extras() { + // This is a description + // Safety: + unsafe {} +} + +fn block_comment() { + /* Safety: */ + unsafe {} +} + +fn block_comment_newlines() { + /* Safety: */ + + unsafe {} +} + +#[rustfmt::skip] +fn inline_block_comment() { + /* Safety: */unsafe {} +} + +fn block_comment_with_extras() { + /* This is a description + * Safety: + */ + unsafe {} +} + +fn block_comment_terminator_same_line() { + /* This is a description + * Safety: */ + unsafe {} +} + +fn buried_safety() { + // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint + // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + // laborum. Safety: + // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi + // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio + // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl + // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. + unsafe {} +} + +fn safety_with_prepended_text() { + // This is a test. Safety: + unsafe {} +} + +fn local_line_comment() { + // Safety: + let _ = unsafe {}; +} + +fn local_block_comment() { + /* Safety: */ + let _ = unsafe {}; +} + +fn comment_array() { + // Safety: + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; +} + +fn comment_tuple() { + // Safety: + let _ = (42, unsafe {}, "test", unsafe {}); +} + +fn comment_unary() { + // Safety: + let _ = *unsafe { &42 }; +} + +#[allow(clippy::match_single_binding)] +fn comment_match() { + // Safety: + let _ = match unsafe {} { + _ => {}, + }; +} + +fn comment_addr_of() { + // Safety: + let _ = &unsafe {}; +} + +fn comment_repeat() { + // Safety: + let _ = [unsafe {}; 5]; +} + +fn comment_macro_call() { + macro_rules! t { + ($b:expr) => { + $b + }; + } + + t!( + // Safety: + unsafe {} + ); +} + +fn comment_macro_def() { + macro_rules! t { + () => { + // Safety: + unsafe {} + }; + } + + t!(); +} + +fn non_ascii_comment() { + // ॐ᧻໒ Safety: ௵∰ + unsafe {}; +} + +fn local_commented_block() { + let _ = + // Safety: + unsafe {}; +} + +fn local_nest() { + // Safety: + let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})]; +} + +// Invalid comments + +fn no_comment() { + unsafe {} +} + +fn no_comment_array() { + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; +} + +fn no_comment_tuple() { + let _ = (42, unsafe {}, "test", unsafe {}); +} + +fn no_comment_unary() { + let _ = *unsafe { &42 }; +} + +#[allow(clippy::match_single_binding)] +fn no_comment_match() { + let _ = match unsafe {} { + _ => {}, + }; +} + +fn no_comment_addr_of() { + let _ = &unsafe {}; +} + +fn no_comment_repeat() { + let _ = [unsafe {}; 5]; +} + +fn local_no_comment() { + let _ = unsafe {}; +} + +fn no_comment_macro_call() { + macro_rules! t { + ($b:expr) => { + $b + }; + } + + t!(unsafe {}); +} + +fn no_comment_macro_def() { + macro_rules! t { + () => { + unsafe {} + }; + } + + t!(); +} + +fn trailing_comment() { + unsafe {} // Safety: +} + +fn internal_comment() { + unsafe { + // Safety: + } +} + +fn interference() { + // Safety + + let _ = 42; + + unsafe {}; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr new file mode 100644 index 00000000000..613e9ffca45 --- /dev/null +++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr @@ -0,0 +1,159 @@ +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:215:5 + | +LL | unsafe {} + | ^^^^^^^^^ + | + = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings` +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + unsafe {} + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:219:5 + | +LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:223:5 + | +LL | let _ = (42, unsafe {}, "test", unsafe {}); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = (42, unsafe {}, "test", unsafe {}); + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:227:5 + | +LL | let _ = *unsafe { &42 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = *unsafe { &42 }; + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:232:5 + | +LL | let _ = match unsafe {} { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = match unsafe {} { + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:238:5 + | +LL | let _ = &unsafe {}; + | ^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = &unsafe {}; + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:242:5 + | +LL | let _ = [unsafe {}; 5]; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = [unsafe {}; 5]; + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:246:5 + | +LL | let _ = unsafe {}; + | ^^^^^^^^^^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + let _ = unsafe {}; + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:256:8 + | +LL | t!(unsafe {}); + | ^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ t!(// Safety: ... +LL ~ unsafe {}); + | + +error: unsafe block in macro expansion missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:262:13 + | +LL | unsafe {} + | ^^^^^^^^^ +... +LL | t!(); + | ---- in this macro invocation + | + = help: consider adding a safety comment in the macro definition + = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:270:5 + | +LL | unsafe {} // Safety: + | ^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL ~ unsafe {} // Safety: + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:274:5 + | +LL | unsafe { + | ^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL + unsafe { + | + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:284:5 + | +LL | unsafe {}; + | ^^^^^^^^^ + | +help: consider adding a safety comment + | +LL ~ // Safety: ... +LL ~ unsafe {}; + | + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs new file mode 100644 index 00000000000..dc150cf28f2 --- /dev/null +++ b/src/tools/clippy/tests/ui/uninit_vec.rs @@ -0,0 +1,94 @@ +#![warn(clippy::uninit_vec)] + +use std::mem::MaybeUninit; + +#[derive(Default)] +struct MyVec { + vec: Vec<u8>, +} + +fn main() { + // with_capacity() -> set_len() should be detected + let mut vec: Vec<u8> = Vec::with_capacity(1000); + unsafe { + vec.set_len(200); + } + + // reserve() -> set_len() should be detected + vec.reserve(1000); + unsafe { + vec.set_len(200); + } + + // new() -> set_len() should be detected + let mut vec: Vec<u8> = Vec::new(); + unsafe { + vec.set_len(200); + } + + // default() -> set_len() should be detected + let mut vec: Vec<u8> = Default::default(); + unsafe { + vec.set_len(200); + } + + let mut vec: Vec<u8> = Vec::default(); + unsafe { + vec.set_len(200); + } + + // test when both calls are enclosed in the same unsafe block + unsafe { + let mut vec: Vec<u8> = Vec::with_capacity(1000); + vec.set_len(200); + + vec.reserve(1000); + vec.set_len(200); + } + + let mut vec: Vec<u8> = Vec::with_capacity(1000); + unsafe { + // test the case where there are other statements in the following unsafe block + vec.set_len(200); + assert!(vec.len() == 200); + } + + // handle vec stored in the field of a struct + let mut my_vec = MyVec::default(); + my_vec.vec.reserve(1000); + unsafe { + my_vec.vec.set_len(200); + } + + my_vec.vec = Vec::with_capacity(1000); + unsafe { + my_vec.vec.set_len(200); + } + + // Test `#[allow(...)]` attributes on inner unsafe block (shouldn't trigger) + let mut vec: Vec<u8> = Vec::with_capacity(1000); + #[allow(clippy::uninit_vec)] + unsafe { + vec.set_len(200); + } + + // MaybeUninit-wrapped types should not be detected + unsafe { + let mut vec: Vec<MaybeUninit<u8>> = Vec::with_capacity(1000); + vec.set_len(200); + + let mut vec: Vec<(MaybeUninit<u8>, MaybeUninit<bool>)> = Vec::with_capacity(1000); + vec.set_len(200); + + let mut vec: Vec<(MaybeUninit<u8>, [MaybeUninit<bool>; 2])> = Vec::with_capacity(1000); + vec.set_len(200); + } + + // known false negative + let mut vec1: Vec<u8> = Vec::with_capacity(1000); + let mut vec2: Vec<u8> = Vec::with_capacity(1000); + unsafe { + vec1.set_len(200); + vec2.set_len(200); + } +} diff --git a/src/tools/clippy/tests/ui/uninit_vec.stderr b/src/tools/clippy/tests/ui/uninit_vec.stderr new file mode 100644 index 00000000000..520bfb26b62 --- /dev/null +++ b/src/tools/clippy/tests/ui/uninit_vec.stderr @@ -0,0 +1,105 @@ +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:12:5 + | +LL | let mut vec: Vec<u8> = Vec::with_capacity(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninit-vec` implied by `-D warnings` + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:18:5 + | +LL | vec.reserve(1000); + | ^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` on empty `Vec` creates out-of-bound values + --> $DIR/uninit_vec.rs:24:5 + | +LL | let mut vec: Vec<u8> = Vec::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + +error: calling `set_len()` on empty `Vec` creates out-of-bound values + --> $DIR/uninit_vec.rs:30:5 + | +LL | let mut vec: Vec<u8> = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + +error: calling `set_len()` on empty `Vec` creates out-of-bound values + --> $DIR/uninit_vec.rs:35:5 + | +LL | let mut vec: Vec<u8> = Vec::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:49:5 + | +LL | let mut vec: Vec<u8> = Vec::with_capacity(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:58:5 + | +LL | my_vec.vec.reserve(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | my_vec.vec.set_len(200); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:63:5 + | +LL | my_vec.vec = Vec::with_capacity(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { +LL | my_vec.vec.set_len(200); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:42:9 + | +LL | let mut vec: Vec<u8> = Vec::with_capacity(1000); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> $DIR/uninit_vec.rs:45:9 + | +LL | vec.reserve(1000); + | ^^^^^^^^^^^^^^^^^^ +LL | vec.set_len(200); + | ^^^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed index b45b27d8f23..d806d620b17 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed @@ -2,6 +2,7 @@ #![allow(clippy::stable_sort_primitive)] +use std::cell::Ref; use std::cmp::Reverse; fn unnecessary_sort_by() { @@ -33,6 +34,10 @@ fn unnecessary_sort_by() { // `Reverse(b)` would borrow in the following cases, don't lint vec.sort_by(|a, b| b.cmp(a)); vec.sort_unstable_by(|a, b| b.cmp(a)); + + // No warning if element does not implement `Ord` + let mut vec: Vec<Ref<usize>> = Vec::new(); + vec.sort_unstable_by(|a, b| a.cmp(b)); } // Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.rs b/src/tools/clippy/tests/ui/unnecessary_sort_by.rs index be2abe7f701..6ee9c3af455 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.rs +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.rs @@ -2,6 +2,7 @@ #![allow(clippy::stable_sort_primitive)] +use std::cell::Ref; use std::cmp::Reverse; fn unnecessary_sort_by() { @@ -33,6 +34,10 @@ fn unnecessary_sort_by() { // `Reverse(b)` would borrow in the following cases, don't lint vec.sort_by(|a, b| b.cmp(a)); vec.sort_unstable_by(|a, b| b.cmp(a)); + + // No warning if element does not implement `Ord` + let mut vec: Vec<Ref<usize>> = Vec::new(); + vec.sort_unstable_by(|a, b| a.cmp(b)); } // Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr index 50607933e18..ca9641e8803 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr @@ -1,5 +1,5 @@ error: use Vec::sort here instead - --> $DIR/unnecessary_sort_by.rs:14:5 + --> $DIR/unnecessary_sort_by.rs:15:5 | LL | vec.sort_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` @@ -7,67 +7,67 @@ LL | vec.sort_by(|a, b| a.cmp(b)); = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` error: use Vec::sort here instead - --> $DIR/unnecessary_sort_by.rs:15:5 + --> $DIR/unnecessary_sort_by.rs:16:5 | LL | vec.sort_unstable_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:16:5 + --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:17:5 + --> $DIR/unnecessary_sort_by.rs:18:5 | LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| id(-a))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:20:5 + --> $DIR/unnecessary_sort_by.rs:21:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:21:5 + --> $DIR/unnecessary_sort_by.rs:22:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|b| Reverse(id(-b)))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:31:5 + --> $DIR/unnecessary_sort_by.rs:32:5 | LL | vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (***a).abs())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:32:5 + --> $DIR/unnecessary_sort_by.rs:33:5 | LL | vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| (***a).abs())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:88:9 + --> $DIR/unnecessary_sort_by.rs:93:9 | LL | args.sort_by(|a, b| a.name().cmp(&b.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|a| a.name())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:89:9 + --> $DIR/unnecessary_sort_by.rs:94:9 | LL | args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|a| a.name())` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:91:9 + --> $DIR/unnecessary_sort_by.rs:96:9 | LL | args.sort_by(|a, b| b.name().cmp(&a.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|b| Reverse(b.name()))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:92:9 + --> $DIR/unnecessary_sort_by.rs:97:9 | LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|b| Reverse(b.name()))` diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed index ee9c9045fff..98bc1e80731 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed @@ -230,4 +230,12 @@ mod super_imports { let _ = foofoo(); } } + + mod attestation_should_be_replaced { + use super::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } } diff --git a/src/tools/clippy/tests/ui/wildcard_imports.rs b/src/tools/clippy/tests/ui/wildcard_imports.rs index efaa8f9ef66..4ef61f9245b 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.rs +++ b/src/tools/clippy/tests/ui/wildcard_imports.rs @@ -231,4 +231,12 @@ mod super_imports { let _ = foofoo(); } } + + mod attestation_should_be_replaced { + use super::*; + + fn with_explicit() { + let _ = foofoo(); + } + } } diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr index 66267dd27b8..d7af0c046e8 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr @@ -122,5 +122,11 @@ error: usage of wildcard import LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` -error: aborting due to 20 previous errors +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:236:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui_test/eq_op.rs b/src/tools/clippy/tests/ui_test/eq_op.rs new file mode 100644 index 00000000000..f2f5f1e588e --- /dev/null +++ b/src/tools/clippy/tests/ui_test/eq_op.rs @@ -0,0 +1,15 @@ +#[warn(clippy::eq_op)] +#[test] +fn eq_op_shouldnt_trigger_in_tests() { + let a = 1; + let result = a + 1 == 1 + a; + assert!(result); +} + +#[test] +fn eq_op_macros_shouldnt_trigger_in_tests() { + let a = 1; + let b = 2; + assert_eq!(a, a); + assert_eq!(a + b, b + a); +} |
