diff options
| author | bors <bors@rust-lang.org> | 2023-10-07 05:18:45 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-10-07 05:18:45 +0000 |
| commit | 1c42857135ca902122fdbe6e050ba1481431541b (patch) | |
| tree | 10b27e1cd9ff220269c414ef591f60a79a74ddf2 | |
| parent | 3b08930677e5f9b5999caa2e5960df8def1b1e73 (diff) | |
| parent | 722736ac4ab71b34645149b6994c0d56c6143832 (diff) | |
| download | rust-1c42857135ca902122fdbe6e050ba1481431541b.tar.gz rust-1c42857135ca902122fdbe6e050ba1481431541b.zip | |
Auto merge of #3113 - rust-lang:rustup-2023-10-07, r=saethlin
Automatic sync from rustc
255 files changed, 7078 insertions, 4889 deletions
diff --git a/Cargo.lock b/Cargo.lock index 60a8f77c07d..db316e998c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -508,10 +508,12 @@ checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "clippy" -version = "0.1.74" +version = "0.1.75" dependencies = [ + "anstream", "clippy_lints", "clippy_utils", + "color-print", "filetime", "futures", "if_chain", @@ -546,7 +548,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.74" +version = "0.1.75" dependencies = [ "arrayvec", "cargo_metadata", @@ -566,11 +568,12 @@ dependencies = [ "unicode-normalization", "unicode-script", "url", + "walkdir", ] [[package]] name = "clippy_utils" -version = "0.1.74" +version = "0.1.75" dependencies = [ "arrayvec", "if_chain", @@ -604,6 +607,27 @@ dependencies = [ ] [[package]] +name = "color-print" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a858372ff14bab9b1b30ea504f2a4bc534582aee3e42ba2d41d2a7baba63d5d" +dependencies = [ + "color-print-proc-macro", +] + +[[package]] +name = "color-print-proc-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e37866456a721d0a404439a1adae37a31be4e0055590d053dfe6981e05003f" +dependencies = [ + "nom", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] name = "color-spantrace" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -933,7 +957,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" [[package]] name = "declare_clippy_lint" -version = "0.1.74" +version = "0.1.75" dependencies = [ "itertools", "quote", @@ -4311,7 +4335,6 @@ dependencies = [ "rustc_errors", "rustc_hir", "rustc_index", - "rustc_macros", "rustc_middle", "rustc_query_system", "rustc_serialize", @@ -4417,7 +4440,6 @@ dependencies = [ "rustc_hir", "rustc_interface", "rustc_middle", - "rustc_session", "rustc_span", "rustc_target", "stable_mir", @@ -4991,6 +5013,7 @@ version = "0.0.0" dependencies = [ "addr2line", "alloc", + "cc", "cfg-if", "compiler_builtins", "core", diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 6f82d6f9323..f013ff45a4f 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -353,28 +353,28 @@ pub fn find_body_stability( body_stab } +fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -> Option<()> { + if item.is_some() { + handle_errors( + &sess.parse_sess, + meta.span, + AttrError::MultipleItem(pprust::path_to_string(&meta.path)), + ); + None + } else if let Some(v) = meta.value_str() { + *item = Some(v); + Some(()) + } else { + sess.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span }); + None + } +} + /// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and /// its stability information. fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, StabilityLevel)> { let meta = attr.meta()?; let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None }; - let insert_or_error = |meta: &MetaItem, item: &mut Option<Symbol>| { - if item.is_some() { - handle_errors( - &sess.parse_sess, - meta.span, - AttrError::MultipleItem(pprust::path_to_string(&meta.path)), - ); - return false; - } - if let Some(v) = meta.value_str() { - *item = Some(v); - true - } else { - sess.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span }); - false - } - }; let mut feature = None; let mut since = None; @@ -389,16 +389,8 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit }; match mi.name_or_empty() { - sym::feature => { - if !insert_or_error(mi, &mut feature) { - return None; - } - } - sym::since => { - if !insert_or_error(mi, &mut since) { - return None; - } - } + sym::feature => insert_or_error(sess, mi, &mut feature)?, + sym::since => insert_or_error(sess, mi, &mut since)?, _ => { handle_errors( &sess.parse_sess, @@ -438,23 +430,6 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, StabilityLevel)> { let meta = attr.meta()?; let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None }; - let insert_or_error = |meta: &MetaItem, item: &mut Option<Symbol>| { - if item.is_some() { - handle_errors( - &sess.parse_sess, - meta.span, - AttrError::MultipleItem(pprust::path_to_string(&meta.path)), - ); - return false; - } - if let Some(v) = meta.value_str() { - *item = Some(v); - true - } else { - sess.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span }); - false - } - }; let mut feature = None; let mut reason = None; @@ -473,20 +448,10 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabil }; match mi.name_or_empty() { - sym::feature => { - if !insert_or_error(mi, &mut feature) { - return None; - } - } - sym::reason => { - if !insert_or_error(mi, &mut reason) { - return None; - } - } + sym::feature => insert_or_error(sess, mi, &mut feature)?, + sym::reason => insert_or_error(sess, mi, &mut reason)?, sym::issue => { - if !insert_or_error(mi, &mut issue) { - return None; - } + insert_or_error(sess, mi, &mut issue)?; // These unwraps are safe because `insert_or_error` ensures the meta item // is a name/value pair string literal. @@ -515,11 +480,7 @@ fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabil } is_soft = true; } - sym::implied_by => { - if !insert_or_error(mi, &mut implied_by) { - return None; - } - } + sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?, _ => { handle_errors( &sess.parse_sess, diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index b7da15af6dc..ff04b0237c2 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -368,7 +368,7 @@ fn check_opaque_type_well_formed<'tcx>( if errors.is_empty() { Ok(definition_ty) } else { - Err(infcx.err_ctxt().report_fulfillment_errors(&errors)) + Err(infcx.err_ctxt().report_fulfillment_errors(errors)) } } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 0a451dad9d2..0c0a1e80a41 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -250,17 +250,6 @@ pub(crate) fn verify_func( } fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { - if let Err(err) = - fx.mir.post_mono_checks(fx.tcx, ty::ParamEnv::reveal_all(), |c| Ok(fx.monomorphize(c))) - { - err.emit_err(fx.tcx); - fx.bcx.append_block_params_for_function_params(fx.block_map[START_BLOCK]); - fx.bcx.switch_to_block(fx.block_map[START_BLOCK]); - // compilation should have been aborted - fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); - return; - } - let arg_uninhabited = fx .mir .args_iter() diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index fde4e85f966..558f64fffc2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -21,6 +21,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } pub fn eval_mir_constant(&self, constant: &mir::ConstOperand<'tcx>) -> mir::ConstValue<'tcx> { + // `MirUsedCollector` visited all constants before codegen began, so if we got here there + // can be no more constants that fail to evaluate. self.monomorphize(constant.const_) .eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), Some(constant.span)) .expect("erroneous constant not captured by required_consts") diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index a61018f9870..d0b799e087b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -209,18 +209,11 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( caller_location: None, }; - fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut start_bx); + // It may seem like we should iterate over `required_consts` to ensure they all successfully + // evaluate; however, the `MirUsedCollector` already did that during the collection phase of + // monomorphization so we don't have to do it again. - // Rust post-monomorphization checks; we later rely on them. - if let Err(err) = - mir.post_mono_checks(cx.tcx(), ty::ParamEnv::reveal_all(), |c| Ok(fx.monomorphize(c))) - { - err.emit_err(cx.tcx()); - // This IR shouldn't ever be emitted, but let's try to guard against any of this code - // ever running. - start_bx.abort(); - return; - } + fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut start_bx); let memory_locals = analyze::non_ssa_locals(&fx); diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index af7dfbef2ff..1c2e8d807f4 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -750,12 +750,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). if M::POST_MONO_CHECKS { - // `ctfe_query` does some error message decoration that we want to be in effect here. - self.ctfe_query(None, |tcx| { - body.post_mono_checks(*tcx, self.param_env, |c| { - self.subst_from_current_frame_and_normalize_erasing_regions(c) - }) - })?; + for &const_ in &body.required_consts { + let c = + self.subst_from_current_frame_and_normalize_erasing_regions(const_.const_)?; + c.eval(*self.tcx, self.param_env, Some(const_.span)).map_err(|err| { + err.emit_note(*self.tcx); + err + })?; + } } // done @@ -1054,14 +1056,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } - /// Call a query that can return `ErrorHandled`. If `span` is `Some`, point to that span when an error occurs. + /// Call a query that can return `ErrorHandled`. Should be used for statics and other globals. + /// (`mir::Const`/`ty::Const` have `eval` methods that can be used directly instead.) pub fn ctfe_query<T>( &self, - span: Option<Span>, query: impl FnOnce(TyCtxtAt<'tcx>) -> Result<T, ErrorHandled>, ) -> Result<T, ErrorHandled> { // Use a precise span for better cycle errors. - query(self.tcx.at(span.unwrap_or_else(|| self.cur_span()))).map_err(|err| { + query(self.tcx.at(self.cur_span())).map_err(|err| { err.emit_note(*self.tcx); err }) @@ -1082,7 +1084,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } else { self.param_env }; - let val = self.ctfe_query(None, |tcx| tcx.eval_to_allocation_raw(param_env.and(gid)))?; + let val = self.ctfe_query(|tcx| tcx.eval_to_allocation_raw(param_env.and(gid)))?; self.raw_const_to_mplace(val) } @@ -1092,7 +1094,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { span: Option<Span>, layout: Option<TyAndLayout<'tcx>>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let const_val = self.ctfe_query(span, |tcx| val.eval(*tcx, self.param_env, span))?; + let const_val = val.eval(*self.tcx, self.param_env, span).map_err(|err| { + // FIXME: somehow this is reachable even when POST_MONO_CHECKS is on. + // Are we not always populating `required_consts`? + err.emit_note(*self.tcx); + err + })?; self.const_val_to_op(const_val, val.ty(), layout) } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 2c0ba9b2673..2c6a4de456d 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -164,7 +164,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::type_name => Ty::new_static_str(self.tcx.tcx), _ => bug!(), }; - let val = self.ctfe_query(None, |tcx| { + let val = self.ctfe_query(|tcx| { tcx.const_eval_global_id(self.param_env, gid, Some(tcx.span)) })?; let val = self.const_val_to_op(val, ty, Some(dest.layout))?; diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 436c4d521a5..ce666e6af3b 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -536,7 +536,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // We don't give a span -- statics don't need that, they cannot be generic or associated. - let val = self.ctfe_query(None, |tcx| tcx.eval_static_initializer(def_id))?; + let val = self.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?; (val, Some(def_id)) } }; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index c4d806c5161..f8f9bfb0470 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -743,7 +743,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors); + infcx.err_ctxt().report_fulfillment_errors(errors); } // Attempting to call a trait method? diff --git a/compiler/rustc_error_codes/src/error_codes/E0282.md b/compiler/rustc_error_codes/src/error_codes/E0282.md index 49d2205f92c..5de43982e8b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0282.md +++ b/compiler/rustc_error_codes/src/error_codes/E0282.md @@ -3,7 +3,7 @@ The compiler could not infer a type and asked for a type annotation. Erroneous code example: ```compile_fail,E0282 -let x = "hello".chars().rev().collect(); +let x = Vec::new(); ``` This error indicates that type inference did not result in one unique possible @@ -11,21 +11,24 @@ type, and extra information is required. In most cases this can be provided by adding a type annotation. Sometimes you need to specify a generic type parameter manually. -A common example is the `collect` method on `Iterator`. It has a generic type -parameter with a `FromIterator` bound, which for a `char` iterator is -implemented by `Vec` and `String` among others. Consider the following snippet -that reverses the characters of a string: +In the example above, type `Vec` has a type parameter `T`. When calling +`Vec::new`, barring any other later usage of the variable `x` that allows the +compiler to infer what type `T` is, the compiler needs to be told what it is. -In the first code example, the compiler cannot infer what the type of `x` should -be: `Vec<char>` and `String` are both suitable candidates. To specify which type -to use, you can use a type annotation on `x`: +The type can be specified on the variable: ``` -let x: Vec<char> = "hello".chars().rev().collect(); +let x: Vec<i32> = Vec::new(); ``` -It is not necessary to annotate the full type. Once the ambiguity is resolved, -the compiler can infer the rest: +The type can also be specified in the path of the expression: + +``` +let x = Vec::<i32>::new(); +``` + +In cases with more complex types, it is not necessary to annotate the full +type. Once the ambiguity is resolved, the compiler can infer the rest: ``` let x: Vec<_> = "hello".chars().rev().collect(); diff --git a/compiler/rustc_error_codes/src/error_codes/E0283.md b/compiler/rustc_error_codes/src/error_codes/E0283.md index 79d2c8204f9..b2f0ede6a0b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0283.md +++ b/compiler/rustc_error_codes/src/error_codes/E0283.md @@ -1,8 +1,52 @@ -An implementation cannot be chosen unambiguously because of lack of information. +The compiler could not infer a type and asked for a type annotation. Erroneous code example: ```compile_fail,E0283 +let x = "hello".chars().rev().collect(); +``` + +This error indicates that type inference did not result in one unique possible +type, and extra information is required. In most cases this can be provided +by adding a type annotation. Sometimes you need to specify a generic type +parameter manually. + +A common example is the `collect` method on `Iterator`. It has a generic type +parameter with a `FromIterator` bound, which for a `char` iterator is +implemented by `Vec` and `String` among others. Consider the following snippet +that reverses the characters of a string: + +In the first code example, the compiler cannot infer what the type of `x` should +be: `Vec<char>` and `String` are both suitable candidates. To specify which type +to use, you can use a type annotation on `x`: + +``` +let x: Vec<char> = "hello".chars().rev().collect(); +``` + +It is not necessary to annotate the full type. Once the ambiguity is resolved, +the compiler can infer the rest: + +``` +let x: Vec<_> = "hello".chars().rev().collect(); +``` + +Another way to provide the compiler with enough information, is to specify the +generic type parameter: + +``` +let x = "hello".chars().rev().collect::<Vec<char>>(); +``` + +Again, you need not specify the full type if the compiler can infer it: + +``` +let x = "hello".chars().rev().collect::<Vec<_>>(); +``` + +We can see a self-contained example below: + +```compile_fail,E0283 struct Foo; impl Into<u32> for Foo { diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 44e1bdb8370..486aac21972 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -327,7 +327,7 @@ fn check_opaque_meets_bounds<'tcx>( // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - let guar = infcx.err_ctxt().report_fulfillment_errors(&errors); + let guar = infcx.err_ctxt().report_fulfillment_errors(errors); return Err(guar); } match origin { @@ -1512,6 +1512,6 @@ pub(super) fn check_generator_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) { let errors = fulfillment_cx.select_all_or_error(&infcx); debug!(?errors); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors); + infcx.err_ctxt().report_fulfillment_errors(errors); } } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 6a1da615055..f1514ecf69c 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -323,7 +323,7 @@ fn compare_method_predicate_entailment<'tcx>( // FIXME(-Ztrait-solver=next): Not needed when the hack below is removed. let errors = ocx.select_where_possible(); if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); + let reported = infcx.err_ctxt().report_fulfillment_errors(errors); return Err(reported); } @@ -394,7 +394,7 @@ fn compare_method_predicate_entailment<'tcx>( }); } CheckImpliedWfMode::Skip => { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); + let reported = infcx.err_ctxt().report_fulfillment_errors(errors); return Err(reported); } } @@ -874,7 +874,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // RPITs. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); + let reported = infcx.err_ctxt().report_fulfillment_errors(errors); return Err(reported); } @@ -2050,7 +2050,7 @@ fn compare_const_predicate_entailment<'tcx>( // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - return Err(infcx.err_ctxt().report_fulfillment_errors(&errors)); + return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } let outlives_env = OutlivesEnvironment::new(param_env); @@ -2143,7 +2143,7 @@ fn compare_type_predicate_entailment<'tcx>( // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); + let reported = infcx.err_ctxt().report_fulfillment_errors(errors); return Err(reported); } @@ -2358,7 +2358,7 @@ pub(super) fn check_type_bounds<'tcx>( // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); + let reported = infcx.err_ctxt().report_fulfillment_errors(errors); return Err(reported); } diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index 3cd3f5bcfdd..6681292c93d 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -158,7 +158,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { ocx.register_bound(cause, param_env, norm_return_ty, term_did); let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors); + infcx.err_ctxt().report_fulfillment_errors(errors); error = true; } // now we can take the return type of the given main function diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 5fa65f33c76..5d67a36288c 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -588,7 +588,7 @@ pub fn check_function_signature<'tcx>( Ok(()) => { let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors); + infcx.err_ctxt().report_fulfillment_errors(errors); return; } } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index de6ca0d61dc..97ebd42d077 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -116,7 +116,7 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( let errors = wfcx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors); + infcx.err_ctxt().report_fulfillment_errors(errors); return; } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index be70acfc35d..e3e724df272 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -261,7 +261,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef } let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors); + infcx.err_ctxt().report_fulfillment_errors(errors); } // Finally, resolve all regions. @@ -470,7 +470,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe ocx.register_obligation(obligation); let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors); + infcx.err_ctxt().report_fulfillment_errors(errors); } // Finally, resolve all regions. diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index dbd38e1b1fe..b4af321fcc9 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -196,7 +196,7 @@ fn get_impl_args( let errors = ocx.select_all_or_error(); if !errors.is_empty() { - let guar = ocx.infcx.err_ctxt().report_fulfillment_errors(&errors); + let guar = ocx.infcx.err_ctxt().report_fulfillment_errors(errors); return Err(guar); } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d9d0dd93010..a00f3c5d0d8 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2958,7 +2958,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // There should be at least one error reported. If not, we // will still delay a span bug in `report_fulfillment_errors`. Ok::<_, NoSolution>(( - self.err_ctxt().report_fulfillment_errors(&errors), + self.err_ctxt().report_fulfillment_errors(errors), impl_trait_ref.args.type_at(1), element_ty, )) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 415920221f5..5dae74a1f9b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -564,7 +564,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !errors.is_empty() { self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); - self.err_ctxt().report_fulfillment_errors(&errors); + self.err_ctxt().report_fulfillment_errors(errors); } } @@ -577,7 +577,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !result.is_empty() { mutate_fulfillment_errors(&mut result); self.adjust_fulfillment_errors_for_expr_obligation(&mut result); - self.err_ctxt().report_fulfillment_errors(&result); + self.err_ctxt().report_fulfillment_errors(result); } } @@ -1477,7 +1477,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { Ok(normalized_ty) => normalized_ty, Err(errors) => { - let guar = self.err_ctxt().report_fulfillment_errors(&errors); + let guar = self.err_ctxt().report_fulfillment_errors(errors); return Ty::new_error(self.tcx,guar); } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index a9029a8cec0..e45108d1713 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -27,7 +27,7 @@ use std::iter; pub enum TypeAnnotationNeeded { /// ```compile_fail,E0282 - /// let x = "hello".chars().rev().collect(); + /// let x; /// ``` E0282, /// An implementation cannot be chosen unambiguously because of lack of information. diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 12aeae17725..572751d9511 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -1,3 +1,8 @@ +//! "Hooks" provide a way for `tcx` functionality to be provided by some downstream crate without +//! everything in rustc having to depend on that crate. This is somewhat similar to queries, but +//! queries come with a lot of machinery for caching and incremental compilation, whereas hooks are +//! just plain function pointers without any of the query magic. + use crate::mir; use crate::query::TyCtxtAt; use crate::ty::{Ty, TyCtxt}; diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index bc464aca5f3..9de40b3f974 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -43,21 +43,6 @@ impl ErrorHandled { } } - pub fn emit_err(&self, tcx: TyCtxt<'_>) -> ErrorGuaranteed { - match self { - &ErrorHandled::Reported(err, span) => { - if !err.is_tainted_by_errors && !span.is_dummy() { - tcx.sess.emit_err(error::ErroneousConstant { span }); - } - err.error - } - &ErrorHandled::TooGeneric(span) => tcx.sess.delay_span_bug( - span, - "encountered TooGeneric error when monomorphic data was expected", - ), - } - } - pub fn emit_note(&self, tcx: TyCtxt<'_>) { match self { &ErrorHandled::Reported(err, span) => { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 7534c9c0a68..34b6250a89d 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2,7 +2,7 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html -use crate::mir::interpret::{AllocRange, ConstAllocation, ErrorHandled, Scalar}; +use crate::mir::interpret::{AllocRange, ConstAllocation, Scalar}; use crate::mir::visit::MirVisitable; use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable}; @@ -568,34 +568,6 @@ impl<'tcx> Body<'tcx> { pub fn is_custom_mir(&self) -> bool { self.injection_phase.is_some() } - - /// *Must* be called once the full substitution for this body is known, to ensure that the body - /// is indeed fit for code generation or consumption more generally. - /// - /// Sadly there's no nice way to represent an "arbitrary normalizer", so we take one for - /// constants specifically. (`Option<GenericArgsRef>` could be used for that, but the fact - /// that `Instance::args_for_mir_body` is private and instead instance exposes normalization - /// functions makes it seem like exposing the generic args is not the intended strategy.) - /// - /// Also sadly, CTFE doesn't even know whether it runs on MIR that is already polymorphic or still monomorphic, - /// so we cannot just immediately ICE on TooGeneric. - /// - /// Returns Ok(()) if everything went fine, and `Err` if a problem occurred and got reported. - pub fn post_mono_checks( - &self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - normalize_const: impl Fn(Const<'tcx>) -> Result<Const<'tcx>, ErrorHandled>, - ) -> Result<(), ErrorHandled> { - // For now, the only thing we have to check is is to ensure that all the constants used in - // the body successfully evaluate. - for &const_ in &self.required_consts { - let c = normalize_const(const_.const_)?; - c.eval(tcx, param_env, Some(const_.span))?; - } - - Ok(()) - } } #[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 9bd682d37ab..95c1848bc9b 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -184,6 +184,8 @@ macro_rules! make_mir_visitor { visit_place_fns!($($mutability)?); + /// This is called for every constant in the MIR body and every `required_consts` + /// (i.e., including consts that have been dead-code-eliminated). fn visit_constant( &mut self, constant: & $($mutability)? ConstOperand<'tcx>, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 1a9f0e8352e..8525a4e74c2 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1426,6 +1426,8 @@ fn collect_used_items<'tcx>( ); } + // Here we rely on the visitor also visiting `required_consts`, so that we evaluate them + // and abort compilation if any of them errors. MirUsedCollector { tcx, body: &body, diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 88a305e8214..e0778f72bfe 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -225,6 +225,10 @@ parse_expected_semi_found_str = expected `;`, found `{$token}` parse_expected_statement_after_outer_attr = expected statement after outer attribute +parse_expected_struct_field = expected one of `,`, `:`, or `{"}"}`, found `{$token}` + .label = expected one of `,`, `:`, or `{"}"}` + .ident_label = while parsing this struct field + parse_expected_trait_in_trait_impl_found_type = expected a trait, found type parse_extern_crate_name_with_dashes = crate name using dashes are not valid in `extern crate` statements diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 7c75e440aaa..aeb4fd0a304 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -431,6 +431,17 @@ pub(crate) struct ExpectedElseBlock { } #[derive(Diagnostic)] +#[diag(parse_expected_struct_field)] +pub(crate) struct ExpectedStructField { + #[primary_span] + #[label] + pub span: Span, + pub token: Token, + #[label(parse_ident_label)] + pub ident_span: Span, +} + +#[derive(Diagnostic)] #[diag(parse_outer_attribute_not_allowed_on_if_else)] pub(crate) struct OuterAttributeNotAllowedOnIfElse { #[primary_span] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f4cee3a661e..91bb2d9eb66 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2834,7 +2834,7 @@ impl<'a> Parser<'a> { )?; let guard = if this.eat_keyword(kw::If) { let if_span = this.prev_token.span; - let mut cond = this.parse_expr_res(Restrictions::ALLOW_LET, None)?; + let mut cond = this.parse_match_guard_condition()?; CondChecker { parser: this, forbid_let_reason: None }.visit_expr(&mut cond); @@ -2860,9 +2860,9 @@ impl<'a> Parser<'a> { { err.span_suggestion( this.token.span, - "try using a fat arrow here", + "use a fat arrow to start a match arm", "=>", - Applicability::MaybeIncorrect, + Applicability::MachineApplicable, ); err.emit(); this.bump(); @@ -2979,6 +2979,33 @@ impl<'a> Parser<'a> { }) } + fn parse_match_guard_condition(&mut self) -> PResult<'a, P<Expr>> { + self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, None).map_err( + |mut err| { + if self.prev_token == token::OpenDelim(Delimiter::Brace) { + let sugg_sp = self.prev_token.span.shrink_to_lo(); + // Consume everything within the braces, let's avoid further parse + // errors. + self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); + let msg = "you might have meant to start a match arm after the match guard"; + if self.eat(&token::CloseDelim(Delimiter::Brace)) { + let applicability = if self.token.kind != token::FatArrow { + // We have high confidence that we indeed didn't have a struct + // literal in the match guard, but rather we had some operation + // that ended in a path, immediately followed by a block that was + // meant to be the match arm. + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + err.span_suggestion_verbose(sugg_sp, msg, "=> ".to_string(), applicability); + } + } + err + }, + ) + } + pub(crate) fn is_builtin(&self) -> bool { self.token.is_keyword(kw::Builtin) && self.look_ahead(1, |t| *t == token::Pound) } @@ -3049,9 +3076,10 @@ impl<'a> Parser<'a> { || self.look_ahead(2, |t| t == &token::Colon) && ( // `{ ident: token, ` cannot start a block. - self.look_ahead(4, |t| t == &token::Comma) || - // `{ ident: ` cannot start a block unless it's a type ascription `ident: Type`. - self.look_ahead(3, |t| !t.can_begin_type()) + self.look_ahead(4, |t| t == &token::Comma) + // `{ ident: ` cannot start a block unless it's a type ascription + // `ident: Type`. + || self.look_ahead(3, |t| !t.can_begin_type()) ) ) } @@ -3091,6 +3119,7 @@ impl<'a> Parser<'a> { let mut fields = ThinVec::new(); let mut base = ast::StructRest::None; let mut recover_async = false; + let in_if_guard = self.restrictions.contains(Restrictions::IN_IF_GUARD); let mut async_block_err = |e: &mut Diagnostic, span: Span| { recover_async = true; @@ -3128,6 +3157,26 @@ impl<'a> Parser<'a> { e.span_label(pth.span, "while parsing this struct"); } + if let Some((ident, _)) = self.token.ident() + && !self.token.is_reserved_ident() + && self.look_ahead(1, |t| { + AssocOp::from_token(&t).is_some() + || matches!(t.kind, token::OpenDelim(_)) + || t.kind == token::Dot + }) + { + // Looks like they tried to write a shorthand, complex expression. + e.span_suggestion_verbose( + self.token.span.shrink_to_lo(), + "try naming a field", + &format!("{ident}: ", ), + Applicability::MaybeIncorrect, + ); + } + if in_if_guard && close_delim == Delimiter::Brace { + return Err(e); + } + if !recover { return Err(e); } @@ -3173,19 +3222,6 @@ impl<'a> Parser<'a> { ",", Applicability::MachineApplicable, ); - } else if is_shorthand - && (AssocOp::from_token(&self.token).is_some() - || matches!(&self.token.kind, token::OpenDelim(_)) - || self.token.kind == token::Dot) - { - // Looks like they tried to write a shorthand, complex expression. - let ident = parsed_field.expect("is_shorthand implies Some").ident; - e.span_suggestion( - ident.span.shrink_to_lo(), - "try naming a field", - &format!("{ident}: "), - Applicability::HasPlaceholders, - ); } } if !recover { @@ -3288,6 +3324,24 @@ impl<'a> Parser<'a> { // Check if a colon exists one ahead. This means we're parsing a fieldname. let is_shorthand = !this.look_ahead(1, |t| t == &token::Colon || t == &token::Eq); + // Proactively check whether parsing the field will be incorrect. + let is_wrong = this.token.is_ident() + && !this.token.is_reserved_ident() + && !this.look_ahead(1, |t| { + t == &token::Colon + || t == &token::Eq + || t == &token::Comma + || t == &token::CloseDelim(Delimiter::Brace) + || t == &token::CloseDelim(Delimiter::Parenthesis) + }); + if is_wrong { + return Err(errors::ExpectedStructField { + span: this.look_ahead(1, |t| t.span), + ident_span: this.token.span, + token: this.look_ahead(1, |t| t.clone()), + } + .into_diagnostic(&self.sess.span_diagnostic)); + } let (ident, expr) = if is_shorthand { // Mimic `x: x` for the `x` field shorthand. let ident = this.parse_ident_common(false)?; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index e84d8f5b358..6c24646f39a 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -52,6 +52,7 @@ bitflags::bitflags! { const NO_STRUCT_LITERAL = 1 << 1; const CONST_EXPR = 1 << 2; const ALLOW_LET = 1 << 3; + const IN_IF_GUARD = 1 << 4; } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 9a7564cb213..52fb193f3da 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2373,7 +2373,7 @@ impl CheckAttrVisitor<'_> { let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors); + infcx.err_ctxt().report_fulfillment_errors(errors); self.abort.set(true); } } diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index e195f9ab6da..2129a98cda3 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -57,7 +57,7 @@ pub fn ensure_wf<'tcx>( ocx.register_obligation(obligation); let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors); + infcx.err_ctxt().report_fulfillment_errors(errors); false } else { // looks WF! diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index a44dd5ede2f..a350e8b2e3a 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -13,7 +13,6 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } -rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_query_system = { path = "../rustc_query_system" } rustc-rayon-core = { version = "0.5.0", optional = true } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d29ab02c1a6..e120f595d92 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -813,7 +813,6 @@ impl Input { FileName::Anon(_) => None, FileName::MacroExpansion(_) => None, FileName::ProcMacroSourceCode(_) => None, - FileName::CfgSpec(_) => None, FileName::CliCrateAttr(_) => None, FileName::Custom(_) => None, FileName::DocTest(path, _) => Some(path), diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml index 4c29f743708..3e0d6baab6a 100644 --- a/compiler/rustc_smir/Cargo.toml +++ b/compiler/rustc_smir/Cargo.toml @@ -4,14 +4,13 @@ version = "0.0.0" edition = "2021" [dependencies] +rustc_driver = { path = "../rustc_driver" } rustc_hir = { path = "../rustc_hir" } +rustc_interface = { path = "../rustc_interface" } rustc_middle = { path = "../rustc_middle" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -rustc_driver = { path = "../rustc_driver" } -rustc_interface = { path = "../rustc_interface" } -rustc_session = {path = "../rustc_session" } -tracing = "0.1" stable_mir = {path = "../stable_mir" } +tracing = "0.1" [features] diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs index b6c36678db5..d10f46fad9e 100644 --- a/compiler/rustc_smir/src/lib.rs +++ b/compiler/rustc_smir/src/lib.rs @@ -10,10 +10,6 @@ html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(allow(unused_variables), deny(warnings))) )] -#![feature(rustc_private)] -#![feature(ptr_metadata)] -#![feature(type_alias_impl_trait)] // Used to define opaque types. -#![feature(intra_doc_pointers)] pub mod rustc_internal; diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 1a9dea99f64..36eb2247253 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -11,7 +11,7 @@ use rustc_driver::{Callbacks, Compilation, RunCompiler}; use rustc_interface::{interface, Queries}; use rustc_middle::mir::interpret::AllocId; use rustc_middle::ty::TyCtxt; -pub use rustc_span::def_id::{CrateNum, DefId}; +use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::Span; use stable_mir::CompilerError; diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 772e09291a1..6fd61e45fcc 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -280,8 +280,7 @@ impl RealFileName { } /// Differentiates between real files and common virtual files. -#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)] -#[derive(Decodable, Encodable)] +#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash, Decodable, Encodable)] pub enum FileName { Real(RealFileName), /// Call to `quote!`. @@ -292,8 +291,6 @@ pub enum FileName { // FIXME(jseyfried) MacroExpansion(Hash64), ProcMacroSourceCode(Hash64), - /// Strings provided as `--cfg [cfgspec]` stored in a `crate_cfg`. - CfgSpec(Hash64), /// Strings provided as crate attributes in the CLI. CliCrateAttr(Hash64), /// Custom sources for explicit parser calls from plugins and drivers. @@ -305,7 +302,6 @@ pub enum FileName { impl From<PathBuf> for FileName { fn from(p: PathBuf) -> Self { - assert!(!p.to_string_lossy().ends_with('>')); FileName::Real(RealFileName::LocalPath(p)) } } @@ -339,7 +335,6 @@ impl fmt::Display for FileNameDisplay<'_> { MacroExpansion(_) => write!(fmt, "<macro expansion>"), Anon(_) => write!(fmt, "<anon>"), ProcMacroSourceCode(_) => write!(fmt, "<proc-macro source code>"), - CfgSpec(_) => write!(fmt, "<cfgspec>"), CliCrateAttr(_) => write!(fmt, "<crate attribute>"), Custom(ref s) => write!(fmt, "<{s}>"), DocTest(ref path, _) => write!(fmt, "{}", path.display()), @@ -365,7 +360,6 @@ impl FileName { Anon(_) | MacroExpansion(_) | ProcMacroSourceCode(_) - | CfgSpec(_) | CliCrateAttr(_) | Custom(_) | QuoteExpansion(_) diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 015e38b2ac0..88f47b03cc8 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -218,7 +218,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { def_id: LocalDefId, ) -> Result<FxIndexSet<Ty<'tcx>>, ErrorGuaranteed> { self.assumed_wf_types(param_env, def_id) - .map_err(|errors| self.infcx.err_ctxt().report_fulfillment_errors(&errors)) + .map_err(|errors| self.infcx.err_ctxt().report_fulfillment_errors(errors)) } pub fn assumed_wf_types( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs new file mode 100644 index 00000000000..b4835b011dd --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs @@ -0,0 +1,275 @@ +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::InferCtxt; +use crate::traits::{Obligation, ObligationCause, ObligationCtxt}; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_hir as hir; +use rustc_hir::Node; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{Span, DUMMY_SP}; + +use super::ArgKind; + +pub use rustc_infer::traits::error_reporting::*; + +pub trait InferCtxtExt<'tcx> { + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)>; + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option<Span>, + expected_args: Vec<ArgKind>, + found_args: Vec<ArgKind>, + is_closure: bool, + closure_pipe_span: Option<Span>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + + /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` + /// in that order, and returns the generic type corresponding to the + /// argument of that trait (corresponding to the closure arguments). + fn type_implements_fn_trait( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, + polarity: ty::ImplPolarity, + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>; +} + +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)> { + let sm = self.tcx.sess.source_map(); + let hir = self.tcx.hir(); + Some(match node { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }), + .. + }) => ( + fn_decl_span, + fn_arg_span, + hir.body(body) + .params + .iter() + .map(|arg| { + if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = + *arg.pat + { + Some(ArgKind::Tuple( + Some(span), + args.iter() + .map(|pat| { + sm.span_to_snippet(pat.span) + .ok() + .map(|snippet| (snippet, "_".to_owned())) + }) + .collect::<Option<Vec<_>>>()?, + )) + } else { + let name = sm.span_to_snippet(arg.pat.span).ok()?; + Some(ArgKind::Arg(name, "_".to_owned())) + } + }) + .collect::<Option<Vec<ArgKind>>>()?, + ), + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) + | Node::TraitItem(&hir::TraitItem { + kind: hir::TraitItemKind::Fn(ref sig, _), .. + }) => ( + sig.span, + None, + sig.decl + .inputs + .iter() + .map(|arg| match arg.kind { + hir::TyKind::Tup(ref tys) => ArgKind::Tuple( + Some(arg.span), + vec![("_".to_owned(), "_".to_owned()); tys.len()], + ), + _ => ArgKind::empty(), + }) + .collect::<Vec<ArgKind>>(), + ), + Node::Ctor(ref variant_data) => { + let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); + (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) + } + _ => panic!("non-FnLike node found: {node:?}"), + }) + } + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option<Span>, + expected_args: Vec<ArgKind>, + found_args: Vec<ArgKind>, + is_closure: bool, + closure_arg_span: Option<Span>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let kind = if is_closure { "closure" } else { "function" }; + + let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { + let arg_length = arguments.len(); + let distinct = matches!(other, &[ArgKind::Tuple(..)]); + match (arg_length, arguments.get(0)) { + (1, Some(ArgKind::Tuple(_, fields))) => { + format!("a single {}-tuple as argument", fields.len()) + } + _ => format!( + "{} {}argument{}", + arg_length, + if distinct && arg_length > 1 { "distinct " } else { "" }, + pluralize!(arg_length) + ), + } + }; + + let expected_str = args_str(&expected_args, &found_args); + let found_str = args_str(&found_args, &expected_args); + + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0593, + "{} is expected to take {}, but it takes {}", + kind, + expected_str, + found_str, + ); + + err.span_label(span, format!("expected {kind} that takes {expected_str}")); + + if let Some(found_span) = found_span { + err.span_label(found_span, format!("takes {found_str}")); + + // Suggest to take and ignore the arguments with expected_args_length `_`s if + // found arguments is empty (assume the user just wants to ignore args in this case). + // For example, if `expected_args_length` is 2, suggest `|_, _|`. + if found_args.is_empty() && is_closure { + let underscores = vec!["_"; expected_args.len()].join(", "); + err.span_suggestion_verbose( + closure_arg_span.unwrap_or(found_span), + format!( + "consider changing the closure to take and ignore the expected argument{}", + pluralize!(expected_args.len()) + ), + format!("|{underscores}|"), + Applicability::MachineApplicable, + ); + } + + if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { + if fields.len() == expected_args.len() { + let sugg = fields + .iter() + .map(|(name, _)| name.to_owned()) + .collect::<Vec<String>>() + .join(", "); + err.span_suggestion_verbose( + found_span, + "change the closure to take multiple arguments instead of a single tuple", + format!("|{sugg}|"), + Applicability::MachineApplicable, + ); + } + } + if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] + && fields.len() == found_args.len() + && is_closure + { + let sugg = format!( + "|({}){}|", + found_args + .iter() + .map(|arg| match arg { + ArgKind::Arg(name, _) => name.to_owned(), + _ => "_".to_owned(), + }) + .collect::<Vec<String>>() + .join(", "), + // add type annotations if available + if found_args.iter().any(|arg| match arg { + ArgKind::Arg(_, ty) => ty != "_", + _ => false, + }) { + format!( + ": ({})", + fields + .iter() + .map(|(_, ty)| ty.to_owned()) + .collect::<Vec<String>>() + .join(", ") + ) + } else { + String::new() + }, + ); + err.span_suggestion_verbose( + found_span, + "change the closure to accept a tuple instead of individual arguments", + sugg, + Applicability::MachineApplicable, + ); + } + } + + err + } + + fn type_implements_fn_trait( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, + polarity: ty::ImplPolarity, + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { + self.commit_if_ok(|_| { + for trait_def_id in [ + self.tcx.lang_items().fn_trait(), + self.tcx.lang_items().fn_mut_trait(), + self.tcx.lang_items().fn_once_trait(), + ] { + let Some(trait_def_id) = trait_def_id else { continue }; + // Make a fresh inference variable so we can determine what the substitutions + // of the trait are. + let var = self.next_ty_var(TypeVariableOrigin { + span: DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }); + // FIXME(effects) + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]); + let obligation = Obligation::new( + self.tcx, + ObligationCause::dummy(), + param_env, + ty.rebind(ty::TraitPredicate { trait_ref, polarity }), + ); + let ocx = ObligationCtxt::new(self); + ocx.register_obligation(obligation); + if ocx.select_all_or_error().is_empty() { + return Ok(( + self.tcx + .fn_trait_kind_from_def_id(trait_def_id) + .expect("expected to map DefId to ClosureKind"), + ty.rebind(self.resolve_vars_if_possible(var)), + )); + } + } + + Err(()) + }) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 9a728b1060f..989c1310b76 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1,58 +1,25 @@ // ignore-tidy-filelength :( mod ambiguity; +mod infer_ctxt_ext; pub mod on_unimplemented; pub mod suggestions; +mod type_err_ctxt_ext; -use super::{ - FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, ObligationCause, - ObligationCauseCode, ObligationCtxt, OutputTypeParameterMismatch, Overflow, - PredicateObligation, SelectionError, TraitNotObjectSafe, -}; -use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch}; -use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::infer::{self, InferCtxt}; +use super::{Obligation, ObligationCause, ObligationCauseCode, PredicateObligation}; +use crate::infer::InferCtxt; use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache}; -use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::specialize::to_pretty_impl_header; -use crate::traits::NormalizeExt; -use on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _}; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; -use rustc_errors::{ - pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, - MultiSpan, Style, -}; use rustc_hir as hir; -use rustc_hir::def::Namespace; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::{GenericParam, Item, Node}; -use rustc_infer::infer::error_reporting::TypeErrCtxt; -use rustc_infer::infer::{InferOk, TypeTrace}; -use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::solve::Goal; -use rustc_middle::traits::{DefiningAnchor, SelectionOutputTypeParameterMismatch}; -use rustc_middle::ty::abstract_const::NotConstEvaluatable; -use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print}; -use rustc_middle::ty::{ - self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, - TypeVisitable, TypeVisitableExt, -}; -use rustc_session::config::{DumpSolverProofTree, TraitSolver}; -use rustc_session::Limit; -use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::symbol::sym; -use rustc_span::{ExpnKind, Span, DUMMY_SP}; -use std::borrow::Cow; -use std::fmt; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::Span; use std::io::Write; -use std::iter; use std::ops::ControlFlow; -use suggestions::TypeErrCtxtExt as _; +pub use self::infer_ctxt_ext::*; +pub use self::type_err_ctxt_ext::*; pub use rustc_infer::traits::error_reporting::*; // When outputting impl candidates, prefer showing those that are more similar. @@ -77,3438 +44,8 @@ enum GetSafeTransmuteErrorAndReason { Error { err_msg: String, safe_transmute_explanation: String }, } -pub trait InferCtxtExt<'tcx> { - /// Given some node representing a fn-like thing in the HIR map, - /// returns a span and `ArgKind` information that describes the - /// arguments it expects. This can be supplied to - /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)>; - - /// Reports an error when the number of arguments needed by a - /// trait match doesn't match the number that the expression - /// provides. - fn report_arg_count_mismatch( - &self, - span: Span, - found_span: Option<Span>, - expected_args: Vec<ArgKind>, - found_args: Vec<ArgKind>, - is_closure: bool, - closure_pipe_span: Option<Span>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; - - /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` - /// in that order, and returns the generic type corresponding to the - /// argument of that trait (corresponding to the closure arguments). - fn type_implements_fn_trait( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: ty::Binder<'tcx, Ty<'tcx>>, - polarity: ty::ImplPolarity, - ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>; -} - -pub trait TypeErrCtxtExt<'tcx> { - fn build_overflow_error<T>( - &self, - predicate: &T, - span: Span, - suggest_increasing_limit: bool, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> - where - T: fmt::Display - + TypeFoldable<TyCtxt<'tcx>> - + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, - <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug; - - fn report_overflow_error<T>( - &self, - predicate: &T, - span: Span, - suggest_increasing_limit: bool, - mutate: impl FnOnce(&mut Diagnostic), - ) -> ! - where - T: fmt::Display - + TypeFoldable<TyCtxt<'tcx>> - + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, - <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug; - - fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed; - - fn report_fulfillment_errors(&self, errors: &[FulfillmentError<'tcx>]) -> ErrorGuaranteed; - - fn report_overflow_obligation<T>( - &self, - obligation: &Obligation<'tcx, T>, - suggest_increasing_limit: bool, - ) -> ! - where - T: ToPredicate<'tcx> + Clone; - - fn suggest_new_overflow_limit(&self, err: &mut Diagnostic); - - fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; - - /// The `root_obligation` parameter should be the `root_obligation` field - /// from a `FulfillmentError`. If no `FulfillmentError` is available, - /// then it should be the same as `obligation`. - fn report_selection_error( - &self, - obligation: PredicateObligation<'tcx>, - root_obligation: &PredicateObligation<'tcx>, - error: &SelectionError<'tcx>, - ); - - fn report_const_param_not_wf( - &self, - ty: Ty<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; -} - -impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { - /// Given some node representing a fn-like thing in the HIR map, - /// returns a span and `ArgKind` information that describes the - /// arguments it expects. This can be supplied to - /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)> { - let sm = self.tcx.sess.source_map(); - let hir = self.tcx.hir(); - Some(match node { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }), - .. - }) => ( - fn_decl_span, - fn_arg_span, - hir.body(body) - .params - .iter() - .map(|arg| { - if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = - *arg.pat - { - Some(ArgKind::Tuple( - Some(span), - args.iter() - .map(|pat| { - sm.span_to_snippet(pat.span) - .ok() - .map(|snippet| (snippet, "_".to_owned())) - }) - .collect::<Option<Vec<_>>>()?, - )) - } else { - let name = sm.span_to_snippet(arg.pat.span).ok()?; - Some(ArgKind::Arg(name, "_".to_owned())) - } - }) - .collect::<Option<Vec<ArgKind>>>()?, - ), - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) - | Node::TraitItem(&hir::TraitItem { - kind: hir::TraitItemKind::Fn(ref sig, _), .. - }) => ( - sig.span, - None, - sig.decl - .inputs - .iter() - .map(|arg| match arg.kind { - hir::TyKind::Tup(ref tys) => ArgKind::Tuple( - Some(arg.span), - vec![("_".to_owned(), "_".to_owned()); tys.len()], - ), - _ => ArgKind::empty(), - }) - .collect::<Vec<ArgKind>>(), - ), - Node::Ctor(ref variant_data) => { - let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); - (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) - } - _ => panic!("non-FnLike node found: {node:?}"), - }) - } - - /// Reports an error when the number of arguments needed by a - /// trait match doesn't match the number that the expression - /// provides. - fn report_arg_count_mismatch( - &self, - span: Span, - found_span: Option<Span>, - expected_args: Vec<ArgKind>, - found_args: Vec<ArgKind>, - is_closure: bool, - closure_arg_span: Option<Span>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let kind = if is_closure { "closure" } else { "function" }; - - let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { - let arg_length = arguments.len(); - let distinct = matches!(other, &[ArgKind::Tuple(..)]); - match (arg_length, arguments.get(0)) { - (1, Some(ArgKind::Tuple(_, fields))) => { - format!("a single {}-tuple as argument", fields.len()) - } - _ => format!( - "{} {}argument{}", - arg_length, - if distinct && arg_length > 1 { "distinct " } else { "" }, - pluralize!(arg_length) - ), - } - }; - - let expected_str = args_str(&expected_args, &found_args); - let found_str = args_str(&found_args, &expected_args); - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0593, - "{} is expected to take {}, but it takes {}", - kind, - expected_str, - found_str, - ); - - err.span_label(span, format!("expected {kind} that takes {expected_str}")); - - if let Some(found_span) = found_span { - err.span_label(found_span, format!("takes {found_str}")); - - // Suggest to take and ignore the arguments with expected_args_length `_`s if - // found arguments is empty (assume the user just wants to ignore args in this case). - // For example, if `expected_args_length` is 2, suggest `|_, _|`. - if found_args.is_empty() && is_closure { - let underscores = vec!["_"; expected_args.len()].join(", "); - err.span_suggestion_verbose( - closure_arg_span.unwrap_or(found_span), - format!( - "consider changing the closure to take and ignore the expected argument{}", - pluralize!(expected_args.len()) - ), - format!("|{underscores}|"), - Applicability::MachineApplicable, - ); - } - - if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { - if fields.len() == expected_args.len() { - let sugg = fields - .iter() - .map(|(name, _)| name.to_owned()) - .collect::<Vec<String>>() - .join(", "); - err.span_suggestion_verbose( - found_span, - "change the closure to take multiple arguments instead of a single tuple", - format!("|{sugg}|"), - Applicability::MachineApplicable, - ); - } - } - if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] - && fields.len() == found_args.len() - && is_closure - { - let sugg = format!( - "|({}){}|", - found_args - .iter() - .map(|arg| match arg { - ArgKind::Arg(name, _) => name.to_owned(), - _ => "_".to_owned(), - }) - .collect::<Vec<String>>() - .join(", "), - // add type annotations if available - if found_args.iter().any(|arg| match arg { - ArgKind::Arg(_, ty) => ty != "_", - _ => false, - }) { - format!( - ": ({})", - fields - .iter() - .map(|(_, ty)| ty.to_owned()) - .collect::<Vec<String>>() - .join(", ") - ) - } else { - String::new() - }, - ); - err.span_suggestion_verbose( - found_span, - "change the closure to accept a tuple instead of individual arguments", - sugg, - Applicability::MachineApplicable, - ); - } - } - - err - } - - fn type_implements_fn_trait( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: ty::Binder<'tcx, Ty<'tcx>>, - polarity: ty::ImplPolarity, - ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { - self.commit_if_ok(|_| { - for trait_def_id in [ - self.tcx.lang_items().fn_trait(), - self.tcx.lang_items().fn_mut_trait(), - self.tcx.lang_items().fn_once_trait(), - ] { - let Some(trait_def_id) = trait_def_id else { continue }; - // Make a fresh inference variable so we can determine what the substitutions - // of the trait are. - let var = self.next_ty_var(TypeVariableOrigin { - span: DUMMY_SP, - kind: TypeVariableOriginKind::MiscVariable, - }); - // FIXME(effects) - let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]); - let obligation = Obligation::new( - self.tcx, - ObligationCause::dummy(), - param_env, - ty.rebind(ty::TraitPredicate { trait_ref, polarity }), - ); - let ocx = ObligationCtxt::new(self); - ocx.register_obligation(obligation); - if ocx.select_all_or_error().is_empty() { - return Ok(( - self.tcx - .fn_trait_kind_from_def_id(trait_def_id) - .expect("expected to map DefId to ClosureKind"), - ty.rebind(self.resolve_vars_if_possible(var)), - )); - } - } - - Err(()) - }) - } -} - -impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { - fn report_fulfillment_errors(&self, errors: &[FulfillmentError<'tcx>]) -> ErrorGuaranteed { - #[derive(Debug)] - struct ErrorDescriptor<'tcx> { - predicate: ty::Predicate<'tcx>, - index: Option<usize>, // None if this is an old error - } - - let mut error_map: FxIndexMap<_, Vec<_>> = self - .reported_trait_errors - .borrow() - .iter() - .map(|(&span, predicates)| { - ( - span, - predicates - .iter() - .map(|&predicate| ErrorDescriptor { predicate, index: None }) - .collect(), - ) - }) - .collect(); - - for (index, error) in errors.iter().enumerate() { - // We want to ignore desugarings here: spans are equivalent even - // if one is the result of a desugaring and the other is not. - let mut span = error.obligation.cause.span; - let expn_data = span.ctxt().outer_expn_data(); - if let ExpnKind::Desugaring(_) = expn_data.kind { - span = expn_data.call_site; - } - - error_map.entry(span).or_default().push(ErrorDescriptor { - predicate: error.obligation.predicate, - index: Some(index), - }); - - self.reported_trait_errors - .borrow_mut() - .entry(span) - .or_default() - .push(error.obligation.predicate); - } - - // We do this in 2 passes because we want to display errors in order, though - // maybe it *is* better to sort errors by span or something. - let mut is_suppressed = vec![false; errors.len()]; - for (_, error_set) in error_map.iter() { - // We want to suppress "duplicate" errors with the same span. - for error in error_set { - if let Some(index) = error.index { - // Suppress errors that are either: - // 1) strictly implied by another error. - // 2) implied by an error with a smaller index. - for error2 in error_set { - if error2.index.is_some_and(|index2| is_suppressed[index2]) { - // Avoid errors being suppressed by already-suppressed - // errors, to prevent all errors from being suppressed - // at once. - continue; - } - - if self.error_implies(error2.predicate, error.predicate) - && !(error2.index >= error.index - && self.error_implies(error.predicate, error2.predicate)) - { - info!("skipping {:?} (implied by {:?})", error, error2); - is_suppressed[index] = true; - break; - } - } - } - } - } - - for from_expansion in [false, true] { - for (error, suppressed) in iter::zip(errors, &is_suppressed) { - if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion { - self.report_fulfillment_error(error); - } - } - } - - self.tcx.sess.delay_span_bug(DUMMY_SP, "expected fulfillment errors") - } - - /// Reports that an overflow has occurred and halts compilation. We - /// halt compilation unconditionally because it is important that - /// overflows never be masked -- they basically represent computations - /// whose result could not be truly determined and thus we can't say - /// if the program type checks or not -- and they are unusual - /// occurrences in any case. - fn report_overflow_error<T>( - &self, - predicate: &T, - span: Span, - suggest_increasing_limit: bool, - mutate: impl FnOnce(&mut Diagnostic), - ) -> ! - where - T: fmt::Display - + TypeFoldable<TyCtxt<'tcx>> - + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, - <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug, - { - let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit); - mutate(&mut err); - err.emit(); - - self.tcx.sess.abort_if_errors(); - bug!(); - } - - fn build_overflow_error<T>( - &self, - predicate: &T, - span: Span, - suggest_increasing_limit: bool, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> - where - T: fmt::Display - + TypeFoldable<TyCtxt<'tcx>> - + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, - <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug, - { - let predicate = self.resolve_vars_if_possible(predicate.clone()); - let mut pred_str = predicate.to_string(); - - if pred_str.len() > 50 { - // We don't need to save the type to a file, we will be talking about this type already - // in a separate note when we explain the obligation, so it will be available that way. - pred_str = predicate - .print(FmtPrinter::new_with_limit( - self.tcx, - Namespace::TypeNS, - rustc_session::Limit(6), - )) - .unwrap() - .into_buffer(); - } - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0275, - "overflow evaluating the requirement `{}`", - pred_str, - ); - - if suggest_increasing_limit { - self.suggest_new_overflow_limit(&mut err); - } - - err - } - - /// Reports that an overflow has occurred and halts compilation. We - /// halt compilation unconditionally because it is important that - /// overflows never be masked -- they basically represent computations - /// whose result could not be truly determined and thus we can't say - /// if the program type checks or not -- and they are unusual - /// occurrences in any case. - fn report_overflow_obligation<T>( - &self, - obligation: &Obligation<'tcx, T>, - suggest_increasing_limit: bool, - ) -> ! - where - T: ToPredicate<'tcx> + Clone, - { - let predicate = obligation.predicate.clone().to_predicate(self.tcx); - let predicate = self.resolve_vars_if_possible(predicate); - self.report_overflow_error( - &predicate, - obligation.cause.span, - suggest_increasing_limit, - |err| { - self.note_obligation_cause_code( - obligation.cause.body_id, - err, - predicate, - obligation.param_env, - obligation.cause.code(), - &mut vec![], - &mut Default::default(), - ); - }, - ); - } - - fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) { - let suggested_limit = match self.tcx.recursion_limit() { - Limit(0) => Limit(2), - limit => limit * 2, - }; - err.help(format!( - "consider increasing the recursion limit by adding a \ - `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)", - suggested_limit, - self.tcx.crate_name(LOCAL_CRATE), - )); - } - - /// Reports that a cycle was detected which led to overflow and halts - /// compilation. This is equivalent to `report_overflow_obligation` except - /// that we can give a more helpful error message (and, in particular, - /// we do not suggest increasing the overflow limit, which is not - /// going to help). - fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { - let cycle = self.resolve_vars_if_possible(cycle.to_owned()); - assert!(!cycle.is_empty()); - - debug!(?cycle, "report_overflow_error_cycle"); - - // The 'deepest' obligation is most likely to have a useful - // cause 'backtrace' - self.report_overflow_obligation( - cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(), - false, - ); - } - - fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed { - let obligation = self.resolve_vars_if_possible(obligation); - let mut err = self.build_overflow_error(&obligation.predicate, obligation.cause.span, true); - self.note_obligation_cause(&mut err, &obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); - err.emit() - } - - fn report_selection_error( - &self, - mut obligation: PredicateObligation<'tcx>, - root_obligation: &PredicateObligation<'tcx>, - error: &SelectionError<'tcx>, - ) { - let tcx = self.tcx; - - if tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError { - dump_proof_tree(root_obligation, self.infcx); - } - - let mut span = obligation.cause.span; - // FIXME: statically guarantee this by tainting after the diagnostic is emitted - self.set_tainted_by_errors( - tcx.sess.delay_span_bug(span, "`report_selection_error` did not emit an error"), - ); - - let mut err = match *error { - SelectionError::Unimplemented => { - // If this obligation was generated as a result of well-formedness checking, see if we - // can get a better error message by performing HIR-based well-formedness checking. - if let ObligationCauseCode::WellFormed(Some(wf_loc)) = - root_obligation.cause.code().peel_derives() - && !obligation.predicate.has_non_region_infer() - { - if let Some(cause) = self - .tcx - .diagnostic_hir_wf_check((tcx.erase_regions(obligation.predicate), *wf_loc)) - { - obligation.cause = cause.clone(); - span = obligation.cause.span; - } - } - - if let ObligationCauseCode::CompareImplItemObligation { - impl_item_def_id, - trait_item_def_id, - kind: _, - } = *obligation.cause.code() - { - self.report_extra_impl_obligation( - span, - impl_item_def_id, - trait_item_def_id, - &format!("`{}`", obligation.predicate), - ) - .emit(); - return; - } - - // Report a const-param specific error - if let ObligationCauseCode::ConstParam(ty) = *obligation.cause.code().peel_derives() - { - self.report_const_param_not_wf(ty, &obligation).emit(); - return; - } - - let bound_predicate = obligation.predicate.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => { - let trait_predicate = bound_predicate.rebind(trait_predicate); - let trait_predicate = self.resolve_vars_if_possible(trait_predicate); - - // FIXME(effects) - let predicate_is_const = false; - - if self.tcx.sess.has_errors().is_some() - && trait_predicate.references_error() - { - return; - } - let trait_ref = trait_predicate.to_poly_trait_ref(); - - let (post_message, pre_message, type_def) = self - .get_parent_trait_ref(obligation.cause.code()) - .map(|(t, s)| { - ( - format!(" in `{t}`"), - format!("within `{t}`, "), - s.map(|s| (format!("within this `{t}`"), s)), - ) - }) - .unwrap_or_default(); - - let OnUnimplementedNote { - message, - label, - note, - parent_label, - append_const_msg, - } = self.on_unimplemented_note(trait_ref, &obligation); - let have_alt_message = message.is_some() || label.is_some(); - let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id()); - let is_unsize = - Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait(); - let (message, note, append_const_msg) = if is_try_conversion { - ( - Some(format!( - "`?` couldn't convert the error to `{}`", - trait_ref.skip_binder().self_ty(), - )), - Some( - "the question mark operation (`?`) implicitly performs a \ - conversion on the error value using the `From` trait" - .to_owned(), - ), - Some(AppendConstMessage::Default), - ) - } else { - (message, note, append_const_msg) - }; - - let err_msg = self.get_standard_error_message( - &trait_predicate, - message, - predicate_is_const, - append_const_msg, - post_message, - ); - - let (err_msg, safe_transmute_explanation) = if Some(trait_ref.def_id()) - == self.tcx.lang_items().transmute_trait() - { - // Recompute the safe transmute reason and use that for the error reporting - match self.get_safe_transmute_error_and_reason( - obligation.clone(), - trait_ref, - span, - ) { - GetSafeTransmuteErrorAndReason::Silent => return, - GetSafeTransmuteErrorAndReason::Error { - err_msg, - safe_transmute_explanation, - } => (err_msg, Some(safe_transmute_explanation)), - } - } else { - (err_msg, None) - }; - - let mut err = struct_span_err!(self.tcx.sess, span, E0277, "{}", err_msg); - - if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { - err.span_label( - ret_span, - format!( - "expected `{}` because of this", - trait_ref.skip_binder().self_ty() - ), - ); - } - - if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() { - self.add_tuple_trait_message( - &obligation.cause.code().peel_derives(), - &mut err, - ); - } - - if Some(trait_ref.def_id()) == tcx.lang_items().drop_trait() - && predicate_is_const - { - err.note("`~const Drop` was renamed to `~const Destruct`"); - err.note("See <https://github.com/rust-lang/rust/pull/94901> for more details"); - } - - let explanation = get_explanation_based_on_obligation( - &obligation, - trait_ref, - &trait_predicate, - pre_message, - ); - - self.check_for_binding_assigned_block_without_tail_expression( - &obligation, - &mut err, - trait_predicate, - ); - if self.suggest_add_reference_to_arg( - &obligation, - &mut err, - trait_predicate, - have_alt_message, - ) { - self.note_obligation_cause(&mut err, &obligation); - err.emit(); - return; - } - if let Some(s) = label { - // If it has a custom `#[rustc_on_unimplemented]` - // error message, let's display it as the label! - err.span_label(span, s); - if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { - // When the self type is a type param We don't need to "the trait - // `std::marker::Sized` is not implemented for `T`" as we will point - // at the type param with a label to suggest constraining it. - err.help(explanation); - } - } else if let Some(custom_explanation) = safe_transmute_explanation { - err.span_label(span, custom_explanation); - } else { - err.span_label(span, explanation); - } - - if let ObligationCauseCode::Coercion { source, target } = - *obligation.cause.code().peel_derives() - { - if Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { - self.suggest_borrowing_for_object_cast( - &mut err, - &root_obligation, - source, - target, - ); - } - } - - let UnsatisfiedConst(unsatisfied_const) = self - .maybe_add_note_for_unsatisfied_const( - &obligation, - trait_ref, - &trait_predicate, - &mut err, - span, - ); - - if let Some((msg, span)) = type_def { - err.span_label(span, msg); - } - if let Some(s) = note { - // If it has a custom `#[rustc_on_unimplemented]` note, let's display it - err.note(s); - } - if let Some(s) = parent_label { - let body = obligation.cause.body_id; - err.span_label(tcx.def_span(body), s); - } - - self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); - self.suggest_dereferencing_index(&obligation, &mut err, trait_predicate); - let mut suggested = - self.suggest_dereferences(&obligation, &mut err, trait_predicate); - suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate); - let impl_candidates = self.find_similar_impl_candidates(trait_predicate); - suggested = if let &[cand] = &impl_candidates[..] { - let cand = cand.trait_ref; - if let (ty::FnPtr(_), ty::FnDef(..)) = - (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) - { - err.span_suggestion( - span.shrink_to_hi(), - format!( - "the trait `{}` is implemented for fn pointer `{}`, try casting using `as`", - cand.print_only_trait_path(), - cand.self_ty(), - ), - format!(" as {}", cand.self_ty()), - Applicability::MaybeIncorrect, - ); - true - } else { - false - } - } else { - false - } || suggested; - suggested |= - self.suggest_remove_reference(&obligation, &mut err, trait_predicate); - suggested |= self.suggest_semicolon_removal( - &obligation, - &mut err, - span, - trait_predicate, - ); - self.note_version_mismatch(&mut err, &trait_ref); - self.suggest_remove_await(&obligation, &mut err); - self.suggest_derive(&obligation, &mut err, trait_predicate); - - if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() { - self.suggest_await_before_try( - &mut err, - &obligation, - trait_predicate, - span, - ); - } - - if self.suggest_add_clone_to_arg(&obligation, &mut err, trait_predicate) { - err.emit(); - return; - } - - if self.suggest_impl_trait(&mut err, &obligation, trait_predicate) { - err.emit(); - return; - } - - if is_unsize { - // If the obligation failed due to a missing implementation of the - // `Unsize` trait, give a pointer to why that might be the case - err.note( - "all implementations of `Unsize` are provided \ - automatically by the compiler, see \ - <https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html> \ - for more information", - ); - } - - let is_fn_trait = tcx.is_fn_trait(trait_ref.def_id()); - let is_target_feature_fn = if let ty::FnDef(def_id, _) = - *trait_ref.skip_binder().self_ty().kind() - { - !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() - } else { - false - }; - if is_fn_trait && is_target_feature_fn { - err.note( - "`#[target_feature]` functions do not implement the `Fn` traits", - ); - } - - self.try_to_add_help_message( - &obligation, - trait_ref, - &trait_predicate, - &mut err, - span, - is_fn_trait, - suggested, - unsatisfied_const, - ); - - // Changing mutability doesn't make a difference to whether we have - // an `Unsize` impl (Fixes ICE in #71036) - if !is_unsize { - self.suggest_change_mut(&obligation, &mut err, trait_predicate); - } - - // If this error is due to `!: Trait` not implemented but `(): Trait` is - // implemented, and fallback has occurred, then it could be due to a - // variable that used to fallback to `()` now falling back to `!`. Issue a - // note informing about the change in behaviour. - if trait_predicate.skip_binder().self_ty().is_never() - && self.fallback_has_occurred - { - let predicate = trait_predicate.map_bound(|trait_pred| { - trait_pred.with_self_ty(self.tcx, Ty::new_unit(self.tcx)) - }); - let unit_obligation = obligation.with(tcx, predicate); - if self.predicate_may_hold(&unit_obligation) { - err.note( - "this error might have been caused by changes to \ - Rust's type-inference algorithm (see issue #48950 \ - <https://github.com/rust-lang/rust/issues/48950> \ - for more information)", - ); - err.help("did you intend to use the type `()` here instead?"); - } - } - - self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause); - self.suggest_desugaring_async_fn_in_trait(&mut err, trait_ref); - - // Return early if the trait is Debug or Display and the invocation - // originates within a standard library macro, because the output - // is otherwise overwhelming and unhelpful (see #85844 for an - // example). - - let in_std_macro = - match obligation.cause.span.ctxt().outer_expn_data().macro_def_id { - Some(macro_def_id) => { - let crate_name = tcx.crate_name(macro_def_id.krate); - crate_name == sym::std || crate_name == sym::core - } - None => false, - }; - - if in_std_macro - && matches!( - self.tcx.get_diagnostic_name(trait_ref.def_id()), - Some(sym::Debug | sym::Display) - ) - { - err.emit(); - return; - } - - err - } - - ty::PredicateKind::Subtype(predicate) => { - // Errors for Subtype predicates show up as - // `FulfillmentErrorCode::CodeSubtypeError`, - // not selection error. - span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) - } - - ty::PredicateKind::Coerce(predicate) => { - // Errors for Coerce predicates show up as - // `FulfillmentErrorCode::CodeSubtypeError`, - // not selection error. - span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate) - } - - ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => { - span_bug!( - span, - "outlives clauses should not error outside borrowck. obligation: `{:?}`", - obligation - ) - } - - ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => { - span_bug!( - span, - "projection clauses should be implied from elsewhere. obligation: `{:?}`", - obligation - ) - } - - ty::PredicateKind::ObjectSafe(trait_def_id) => { - let violations = self.tcx.object_safety_violations(trait_def_id); - report_object_safety_error(self.tcx, span, trait_def_id, violations) - } - - ty::PredicateKind::ClosureKind(closure_def_id, closure_args, kind) => { - let found_kind = self.closure_kind(closure_args).unwrap(); - self.report_closure_error(&obligation, closure_def_id, found_kind, kind) - } - - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => { - let ty = self.resolve_vars_if_possible(ty); - match self.tcx.sess.opts.unstable_opts.trait_solver { - TraitSolver::Classic => { - // WF predicates cannot themselves make - // errors. They can only block due to - // ambiguity; otherwise, they always - // degenerate into other obligations - // (which may fail). - span_bug!(span, "WF predicate not satisfied for {:?}", ty); - } - TraitSolver::Next | TraitSolver::NextCoherence => { - // FIXME: we'll need a better message which takes into account - // which bounds actually failed to hold. - self.tcx.sess.struct_span_err( - span, - format!("the type `{ty}` is not well-formed"), - ) - } - } - } - - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) => { - // Errors for `ConstEvaluatable` predicates show up as - // `SelectionError::ConstEvalFailure`, - // not `Unimplemented`. - span_bug!( - span, - "const-evaluatable requirement gave wrong error: `{:?}`", - obligation - ) - } - - ty::PredicateKind::ConstEquate(..) => { - // Errors for `ConstEquate` predicates show up as - // `SelectionError::ConstEvalFailure`, - // not `Unimplemented`. - span_bug!( - span, - "const-equate requirement gave wrong error: `{:?}`", - obligation - ) - } - - ty::PredicateKind::Ambiguous => span_bug!(span, "ambiguous"), - - ty::PredicateKind::AliasRelate(..) => span_bug!( - span, - "AliasRelate predicate should never be the predicate cause of a SelectionError" - ), - - ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { - let mut diag = self.tcx.sess.struct_span_err( - span, - format!("the constant `{ct}` is not of type `{ty}`"), - ); - self.note_type_err( - &mut diag, - &obligation.cause, - None, - None, - TypeError::Sorts(ty::error::ExpectedFound::new(true, ty, ct.ty())), - false, - false, - ); - diag - } - } - } - - OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch { - found_trait_ref, - expected_trait_ref, - terr: terr @ TypeError::CyclicTy(_), - }) => self.report_type_parameter_mismatch_cyclic_type_error( - &obligation, - found_trait_ref, - expected_trait_ref, - terr, - ), - OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch { - found_trait_ref, - expected_trait_ref, - terr: _, - }) => { - match self.report_type_parameter_mismatch_error( - &obligation, - span, - found_trait_ref, - expected_trait_ref, - ) { - Some(err) => err, - None => return, - } - } - - SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => self.report_opaque_type_auto_trait_leakage( - &obligation, - def_id, - ), - - TraitNotObjectSafe(did) => { - let violations = self.tcx.object_safety_violations(did); - report_object_safety_error(self.tcx, span, did, violations) - } - - SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsInfer) => { - bug!( - "MentionsInfer should have been handled in `traits/fulfill.rs` or `traits/select/mod.rs`" - ) - } - SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsParam) => { - match self.report_not_const_evaluatable_error(&obligation, span) { - Some(err) => err, - None => return, - } - } - - // Already reported in the query. - SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) | - // Already reported. - Overflow(OverflowError::Error(_)) => return, - - Overflow(_) => { - bug!("overflow should be handled before the `report_selection_error` path"); - } - SelectionError::ErrorReporting => { - bug!("ErrorReporting Overflow should not reach `report_selection_err` call") - } - }; - - self.note_obligation_cause(&mut err, &obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); - err.emit(); - } - - fn report_const_param_not_wf( - &self, - ty: Ty<'tcx>, - obligation: &PredicateObligation<'tcx>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let span = obligation.cause.span; - - let mut diag = match ty.kind() { - _ if ty.has_param() => { - span_bug!(span, "const param tys cannot mention other generic parameters"); - } - ty::Float(_) => { - struct_span_err!( - self.tcx.sess, - span, - E0741, - "`{ty}` is forbidden as the type of a const generic parameter", - ) - } - ty::FnPtr(_) => { - struct_span_err!( - self.tcx.sess, - span, - E0741, - "using function pointers as const generic parameters is forbidden", - ) - } - ty::RawPtr(_) => { - struct_span_err!( - self.tcx.sess, - span, - E0741, - "using raw pointers as const generic parameters is forbidden", - ) - } - ty::Adt(def, _) => { - // We should probably see if we're *allowed* to derive `ConstParamTy` on the type... - let mut diag = struct_span_err!( - self.tcx.sess, - span, - E0741, - "`{ty}` must implement `ConstParamTy` to be used as the type of a const generic parameter", - ); - // Only suggest derive if this isn't a derived obligation, - // and the struct is local. - if let Some(span) = self.tcx.hir().span_if_local(def.did()) - && obligation.cause.code().parent().is_none() - { - if ty.is_structural_eq_shallow(self.tcx) { - diag.span_suggestion( - span, - "add `#[derive(ConstParamTy)]` to the struct", - "#[derive(ConstParamTy)]\n", - Applicability::MachineApplicable, - ); - } else { - // FIXME(adt_const_params): We should check there's not already an - // overlapping `Eq`/`PartialEq` impl. - diag.span_suggestion( - span, - "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct", - "#[derive(ConstParamTy, PartialEq, Eq)]\n", - Applicability::MachineApplicable, - ); - } - } - diag - } - _ => { - struct_span_err!( - self.tcx.sess, - span, - E0741, - "`{ty}` can't be used as a const parameter type", - ) - } - }; - - let mut code = obligation.cause.code(); - let mut pred = obligation.predicate.to_opt_poly_trait_pred(); - while let Some((next_code, next_pred)) = code.parent() { - if let Some(pred) = pred { - let pred = self.instantiate_binder_with_placeholders(pred); - diag.note(format!( - "`{}` must implement `{}`, but it does not", - pred.self_ty(), - pred.print_modifiers_and_trait_path() - )); - } - code = next_code; - pred = next_pred; - } - - diag - } -} - -trait InferCtxtPrivExt<'tcx> { - // returns if `cond` not occurring implies that `error` does not occur - i.e., that - // `error` occurring implies that `cond` occurs. - fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool; - - fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>); - - fn report_projection_error( - &self, - obligation: &PredicateObligation<'tcx>, - error: &MismatchedProjectionTypes<'tcx>, - ); - - fn maybe_detailed_projection_msg( - &self, - pred: ty::ProjectionPredicate<'tcx>, - normalized_ty: ty::Term<'tcx>, - expected_ty: ty::Term<'tcx>, - ) -> Option<String>; - - fn fuzzy_match_tys( - &self, - a: Ty<'tcx>, - b: Ty<'tcx>, - ignoring_lifetimes: bool, - ) -> Option<CandidateSimilarity>; - - fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str>; - - fn find_similar_impl_candidates( - &self, - trait_pred: ty::PolyTraitPredicate<'tcx>, - ) -> Vec<ImplCandidate<'tcx>>; - - fn report_similar_impl_candidates( - &self, - impl_candidates: &[ImplCandidate<'tcx>], - trait_ref: ty::PolyTraitRef<'tcx>, - body_def_id: LocalDefId, - err: &mut Diagnostic, - other: bool, - param_env: ty::ParamEnv<'tcx>, - ) -> bool; - - fn report_similar_impl_candidates_for_root_obligation( - &self, - obligation: &PredicateObligation<'tcx>, - trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, - body_def_id: LocalDefId, - err: &mut Diagnostic, - ); - - /// Gets the parent trait chain start - fn get_parent_trait_ref( - &self, - code: &ObligationCauseCode<'tcx>, - ) -> Option<(String, Option<Span>)>; - - /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait - /// with the same path as `trait_ref`, a help message about - /// a probable version mismatch is added to `err` - fn note_version_mismatch( - &self, - err: &mut Diagnostic, - trait_ref: &ty::PolyTraitRef<'tcx>, - ) -> bool; - - /// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the - /// `trait_ref`. - /// - /// For this to work, `new_self_ty` must have no escaping bound variables. - fn mk_trait_obligation_with_new_self_ty( - &self, - param_env: ty::ParamEnv<'tcx>, - trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, - ) -> PredicateObligation<'tcx>; - - fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>); - - fn predicate_can_apply( - &self, - param_env: ty::ParamEnv<'tcx>, - pred: ty::PolyTraitPredicate<'tcx>, - ) -> bool; - - fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>); - - fn suggest_unsized_bound_if_applicable( - &self, - err: &mut Diagnostic, - obligation: &PredicateObligation<'tcx>, - ); - - fn annotate_source_of_ambiguity( - &self, - err: &mut Diagnostic, - impls: &[ambiguity::Ambiguity], - predicate: ty::Predicate<'tcx>, - ); - - fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>); - - fn maybe_indirection_for_unsized( - &self, - err: &mut Diagnostic, - item: &'tcx Item<'tcx>, - param: &'tcx GenericParam<'tcx>, - ) -> bool; - - fn is_recursive_obligation( - &self, - obligated_types: &mut Vec<Ty<'tcx>>, - cause_code: &ObligationCauseCode<'tcx>, - ) -> bool; - - fn get_standard_error_message( - &self, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, - message: Option<String>, - predicate_is_const: bool, - append_const_msg: Option<AppendConstMessage>, - post_message: String, - ) -> String; - - fn get_safe_transmute_error_and_reason( - &self, - obligation: PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - span: Span, - ) -> GetSafeTransmuteErrorAndReason; - - fn add_tuple_trait_message( - &self, - obligation_cause_code: &ObligationCauseCode<'tcx>, - err: &mut Diagnostic, - ); - - fn try_to_add_help_message( - &self, - obligation: &PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, - err: &mut Diagnostic, - span: Span, - is_fn_trait: bool, - suggested: bool, - unsatisfied_const: bool, - ); - - fn add_help_message_for_fn_trait( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - err: &mut Diagnostic, - implemented_kind: ty::ClosureKind, - params: ty::Binder<'tcx, Ty<'tcx>>, - ); - - fn maybe_add_note_for_unsatisfied_const( - &self, - obligation: &PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, - err: &mut Diagnostic, - span: Span, - ) -> UnsatisfiedConst; - - fn report_closure_error( - &self, - obligation: &PredicateObligation<'tcx>, - closure_def_id: DefId, - found_kind: ty::ClosureKind, - kind: ty::ClosureKind, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; - - fn report_type_parameter_mismatch_cyclic_type_error( - &self, - obligation: &PredicateObligation<'tcx>, - found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - terr: TypeError<'tcx>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; - - fn report_opaque_type_auto_trait_leakage( - &self, - obligation: &PredicateObligation<'tcx>, - def_id: DefId, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; - - fn report_type_parameter_mismatch_error( - &self, - obligation: &PredicateObligation<'tcx>, - span: Span, - found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>; - - fn report_not_const_evaluatable_error( - &self, - obligation: &PredicateObligation<'tcx>, - span: Span, - ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>; -} - -impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { - // returns if `cond` not occurring implies that `error` does not occur - i.e., that - // `error` occurring implies that `cond` occurs. - fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool { - if cond == error { - return true; - } - - // FIXME: It should be possible to deal with `ForAll` in a cleaner way. - let bound_error = error.kind(); - let (cond, error) = match (cond.kind().skip_binder(), bound_error.skip_binder()) { - ( - ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)), - ty::PredicateKind::Clause(ty::ClauseKind::Trait(error)), - ) => (cond, bound_error.rebind(error)), - _ => { - // FIXME: make this work in other cases too. - return false; - } - }; - - for pred in super::elaborate(self.tcx, std::iter::once(cond)) { - let bound_predicate = pred.kind(); - if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(implication)) = - bound_predicate.skip_binder() - { - let error = error.to_poly_trait_ref(); - let implication = bound_predicate.rebind(implication.trait_ref); - // FIXME: I'm just not taking associated types at all here. - // Eventually I'll need to implement param-env-aware - // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic. - let param_env = ty::ParamEnv::empty(); - if self.can_sub(param_env, error, implication) { - debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication); - return true; - } - } - } - - false - } - - #[instrument(skip(self), level = "debug")] - fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) { - if self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError { - dump_proof_tree(&error.root_obligation, self.infcx); - } - - match error.code { - FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { - self.report_selection_error( - error.obligation.clone(), - &error.root_obligation, - selection_error, - ); - } - FulfillmentErrorCode::CodeProjectionError(ref e) => { - self.report_projection_error(&error.obligation, e); - } - FulfillmentErrorCode::CodeAmbiguity { overflow: false } => { - self.maybe_report_ambiguity(&error.obligation); - } - FulfillmentErrorCode::CodeAmbiguity { overflow: true } => { - self.report_overflow_no_abort(error.obligation.clone()); - } - FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { - self.report_mismatched_types( - &error.obligation.cause, - expected_found.expected, - expected_found.found, - *err, - ) - .emit(); - } - FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => { - let mut diag = self.report_mismatched_consts( - &error.obligation.cause, - expected_found.expected, - expected_found.found, - *err, - ); - let code = error.obligation.cause.code().peel_derives().peel_match_impls(); - if let ObligationCauseCode::BindingObligation(..) - | ObligationCauseCode::ItemObligation(..) - | ObligationCauseCode::ExprBindingObligation(..) - | ObligationCauseCode::ExprItemObligation(..) = code - { - self.note_obligation_cause_code( - error.obligation.cause.body_id, - &mut diag, - error.obligation.predicate, - error.obligation.param_env, - code, - &mut vec![], - &mut Default::default(), - ); - } - diag.emit(); - } - FulfillmentErrorCode::CodeCycle(ref cycle) => { - self.report_overflow_obligation_cycle(cycle); - } - } - } - - #[instrument(level = "debug", skip_all)] - fn report_projection_error( - &self, - obligation: &PredicateObligation<'tcx>, - error: &MismatchedProjectionTypes<'tcx>, - ) { - let predicate = self.resolve_vars_if_possible(obligation.predicate); - - if predicate.references_error() { - return; - } - - self.probe(|_| { - let ocx = ObligationCtxt::new(self); - - // try to find the mismatched types to report the error with. - // - // this can fail if the problem was higher-ranked, in which - // cause I have no idea for a good error message. - let bound_predicate = predicate.kind(); - let (values, err) = if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) = - bound_predicate.skip_binder() - { - let data = self.instantiate_binder_with_fresh_vars( - obligation.cause.span, - infer::LateBoundRegionConversionTime::HigherRankedType, - bound_predicate.rebind(data), - ); - let unnormalized_term = match data.term.unpack() { - ty::TermKind::Ty(_) => Ty::new_projection( - self.tcx, - data.projection_ty.def_id, - data.projection_ty.args, - ) - .into(), - ty::TermKind::Const(ct) => ty::Const::new_unevaluated( - self.tcx, - ty::UnevaluatedConst { - def: data.projection_ty.def_id, - args: data.projection_ty.args, - }, - ct.ty(), - ) - .into(), - }; - // FIXME(-Ztrait-solver=next): For diagnostic purposes, it would be nice - // to deeply normalize this type. - let normalized_term = - ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); - - debug!(?obligation.cause, ?obligation.param_env); - - debug!(?normalized_term, data.ty = ?data.term); - - let is_normalized_term_expected = !matches!( - obligation.cause.code().peel_derives(), - ObligationCauseCode::ItemObligation(_) - | ObligationCauseCode::BindingObligation(_, _) - | ObligationCauseCode::ExprItemObligation(..) - | ObligationCauseCode::ExprBindingObligation(..) - | ObligationCauseCode::Coercion { .. } - | ObligationCauseCode::OpaqueType - ); - - // constrain inference variables a bit more to nested obligations from normalize so - // we can have more helpful errors. - // - // we intentionally drop errors from normalization here, - // since the normalization is just done to improve the error message. - let _ = ocx.select_where_possible(); - - if let Err(new_err) = ocx.eq_exp( - &obligation.cause, - obligation.param_env, - is_normalized_term_expected, - normalized_term, - data.term, - ) { - (Some((data, is_normalized_term_expected, normalized_term, data.term)), new_err) - } else { - (None, error.err) - } - } else { - (None, error.err) - }; - - let msg = values - .and_then(|(predicate, _, normalized_term, expected_term)| { - self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term) - }) - .unwrap_or_else(|| { - with_forced_trimmed_paths!(format!( - "type mismatch resolving `{}`", - self.resolve_vars_if_possible(predicate) - .print(FmtPrinter::new_with_limit( - self.tcx, - Namespace::TypeNS, - rustc_session::Limit(10), - )) - .unwrap() - .into_buffer() - )) - }); - let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}"); - - let secondary_span = (|| { - let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) = - predicate.kind().skip_binder() - else { - return None; - }; - - let trait_assoc_item = self.tcx.opt_associated_item(proj.projection_ty.def_id)?; - let trait_assoc_ident = trait_assoc_item.ident(self.tcx); - - let mut associated_items = vec![]; - self.tcx.for_each_relevant_impl( - self.tcx.trait_of_item(proj.projection_ty.def_id)?, - proj.projection_ty.self_ty(), - |impl_def_id| { - associated_items.extend( - self.tcx - .associated_items(impl_def_id) - .in_definition_order() - .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident), - ); - }, - ); - - let [associated_item]: &[ty::AssocItem] = &associated_items[..] else { - return None; - }; - match self.tcx.hir().get_if_local(associated_item.def_id) { - Some( - hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Type(_, Some(ty)), - .. - }) - | hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Type(ty), - .. - }), - ) => Some(( - ty.span, - with_forced_trimmed_paths!(Cow::from(format!( - "type mismatch resolving `{}`", - self.resolve_vars_if_possible(predicate) - .print(FmtPrinter::new_with_limit( - self.tcx, - Namespace::TypeNS, - rustc_session::Limit(5), - )) - .unwrap() - .into_buffer() - ))), - )), - _ => None, - } - })(); - - self.note_type_err( - &mut diag, - &obligation.cause, - secondary_span, - values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| { - infer::ValuePairs::Terms(ExpectedFound::new( - is_normalized_ty_expected, - normalized_ty, - expected_ty, - )) - }), - err, - true, - false, - ); - self.note_obligation_cause(&mut diag, obligation); - diag.emit(); - }); - } - - fn maybe_detailed_projection_msg( - &self, - pred: ty::ProjectionPredicate<'tcx>, - normalized_ty: ty::Term<'tcx>, - expected_ty: ty::Term<'tcx>, - ) -> Option<String> { - let trait_def_id = pred.projection_ty.trait_def_id(self.tcx); - let self_ty = pred.projection_ty.self_ty(); - - with_forced_trimmed_paths! { - if Some(pred.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() { - let fn_kind = self_ty.prefix_string(self.tcx); - let item = match self_ty.kind() { - ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(), - _ => self_ty.to_string(), - }; - Some(format!( - "expected `{item}` to be a {fn_kind} that returns `{expected_ty}`, but it \ - returns `{normalized_ty}`", - )) - } else if Some(trait_def_id) == self.tcx.lang_items().future_trait() { - Some(format!( - "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \ - resolves to `{normalized_ty}`" - )) - } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) { - Some(format!( - "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it \ - yields `{normalized_ty}`" - )) - } else { - None - } - } - } - - fn fuzzy_match_tys( - &self, - mut a: Ty<'tcx>, - mut b: Ty<'tcx>, - ignoring_lifetimes: bool, - ) -> Option<CandidateSimilarity> { - /// returns the fuzzy category of a given type, or None - /// if the type can be equated to any type. - fn type_category(tcx: TyCtxt<'_>, t: Ty<'_>) -> Option<u32> { - match t.kind() { - ty::Bool => Some(0), - ty::Char => Some(1), - ty::Str => Some(2), - ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => Some(2), - ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) => Some(4), - ty::Ref(..) | ty::RawPtr(..) => Some(5), - ty::Array(..) | ty::Slice(..) => Some(6), - ty::FnDef(..) | ty::FnPtr(..) => Some(7), - ty::Dynamic(..) => Some(8), - ty::Closure(..) => Some(9), - ty::Tuple(..) => Some(10), - ty::Param(..) => Some(11), - ty::Alias(ty::Projection, ..) => Some(12), - ty::Alias(ty::Inherent, ..) => Some(13), - ty::Alias(ty::Opaque, ..) => Some(14), - ty::Alias(ty::Weak, ..) => Some(15), - ty::Never => Some(16), - ty::Adt(..) => Some(17), - ty::Generator(..) => Some(18), - ty::Foreign(..) => Some(19), - ty::GeneratorWitness(..) => Some(20), - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, - } - } - - let strip_references = |mut t: Ty<'tcx>| -> Ty<'tcx> { - loop { - match t.kind() { - ty::Ref(_, inner, _) | ty::RawPtr(ty::TypeAndMut { ty: inner, .. }) => { - t = *inner - } - _ => break t, - } - } - }; - - if !ignoring_lifetimes { - a = strip_references(a); - b = strip_references(b); - } - - let cat_a = type_category(self.tcx, a)?; - let cat_b = type_category(self.tcx, b)?; - if a == b { - Some(CandidateSimilarity::Exact { ignoring_lifetimes }) - } else if cat_a == cat_b { - match (a.kind(), b.kind()) { - (ty::Adt(def_a, _), ty::Adt(def_b, _)) => def_a == def_b, - (ty::Foreign(def_a), ty::Foreign(def_b)) => def_a == def_b, - // Matching on references results in a lot of unhelpful - // suggestions, so let's just not do that for now. - // - // We still upgrade successful matches to `ignoring_lifetimes: true` - // to prioritize that impl. - (ty::Ref(..) | ty::RawPtr(..), ty::Ref(..) | ty::RawPtr(..)) => { - self.fuzzy_match_tys(a, b, true).is_some() - } - _ => true, - } - .then_some(CandidateSimilarity::Fuzzy { ignoring_lifetimes }) - } else if ignoring_lifetimes { - None - } else { - self.fuzzy_match_tys(a, b, true) - } - } - - fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str> { - self.tcx.hir().body(body_id).generator_kind.map(|gen_kind| match gen_kind { - hir::GeneratorKind::Gen => "a generator", - hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "an async block", - hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "an async function", - hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "an async closure", - }) - } - - fn find_similar_impl_candidates( - &self, - trait_pred: ty::PolyTraitPredicate<'tcx>, - ) -> Vec<ImplCandidate<'tcx>> { - let mut candidates: Vec<_> = self - .tcx - .all_impls(trait_pred.def_id()) - .filter_map(|def_id| { - if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative - || !self.tcx.is_user_visible_dep(def_id.krate) - { - return None; - } - - let imp = self.tcx.impl_trait_ref(def_id).unwrap().skip_binder(); - - self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false).map( - |similarity| ImplCandidate { trait_ref: imp, similarity, impl_def_id: def_id }, - ) - }) - .collect(); - if candidates.iter().any(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })) { - // If any of the candidates is a perfect match, we don't want to show all of them. - // This is particularly relevant for the case of numeric types (as they all have the - // same category). - candidates.retain(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })); - } - candidates - } - - fn report_similar_impl_candidates( - &self, - impl_candidates: &[ImplCandidate<'tcx>], - trait_ref: ty::PolyTraitRef<'tcx>, - body_def_id: LocalDefId, - err: &mut Diagnostic, - other: bool, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - // If we have a single implementation, try to unify it with the trait ref - // that failed. This should uncover a better hint for what *is* implemented. - if let [single] = &impl_candidates { - if self.probe(|_| { - let ocx = ObligationCtxt::new(self); - let obligation_trait_ref = self.instantiate_binder_with_placeholders(trait_ref); - let impl_args = self.fresh_args_for_item(DUMMY_SP, single.impl_def_id); - let impl_trait_ref = ocx.normalize( - &ObligationCause::dummy(), - param_env, - ty::EarlyBinder::bind(single.trait_ref).instantiate(self.tcx, impl_args), - ); - - ocx.register_obligations( - self.tcx - .predicates_of(single.impl_def_id) - .instantiate(self.tcx, impl_args) - .into_iter() - .map(|(clause, _)| { - Obligation::new(self.tcx, ObligationCause::dummy(), param_env, clause) - }), - ); - if !ocx.select_where_possible().is_empty() { - return false; - } - - let mut terrs = vec![]; - for (obligation_arg, impl_arg) in - std::iter::zip(obligation_trait_ref.args, impl_trait_ref.args) - { - if let Err(terr) = - ocx.eq(&ObligationCause::dummy(), param_env, impl_arg, obligation_arg) - { - terrs.push(terr); - } - if !ocx.select_where_possible().is_empty() { - return false; - } - } - - // Literally nothing unified, just give up. - if terrs.len() == impl_trait_ref.args.len() { - return false; - } - - let cand = - self.resolve_vars_if_possible(impl_trait_ref).fold_with(&mut BottomUpFolder { - tcx: self.tcx, - ty_op: |ty| ty, - lt_op: |lt| lt, - ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()), - }); - err.highlighted_help(vec![ - (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle), - ("is".to_string(), Style::Highlight), - (" implemented for `".to_string(), Style::NoStyle), - (cand.self_ty().to_string(), Style::Highlight), - ("`".to_string(), Style::NoStyle), - ]); - - if let [TypeError::Sorts(exp_found)] = &terrs[..] { - let exp_found = self.resolve_vars_if_possible(*exp_found); - err.help(format!( - "for that trait implementation, expected `{}`, found `{}`", - exp_found.expected, exp_found.found - )); - } - - true - }) { - return true; - } - } - - let other = if other { "other " } else { "" }; - let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| { - if candidates.is_empty() { - return false; - } - if let &[cand] = &candidates[..] { - let (desc, mention_castable) = - match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) { - (ty::FnPtr(_), ty::FnDef(..)) => { - (" implemented for fn pointer `", ", cast using `as`") - } - (ty::FnPtr(_), _) => (" implemented for fn pointer `", ""), - _ => (" implemented for `", ""), - }; - err.highlighted_help(vec![ - (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle), - ("is".to_string(), Style::Highlight), - (desc.to_string(), Style::NoStyle), - (cand.self_ty().to_string(), Style::Highlight), - ("`".to_string(), Style::NoStyle), - (mention_castable.to_string(), Style::NoStyle), - ]); - return true; - } - let trait_ref = TraitRef::identity(self.tcx, candidates[0].def_id); - // Check if the trait is the same in all cases. If so, we'll only show the type. - let mut traits: Vec<_> = - candidates.iter().map(|c| c.print_only_trait_path().to_string()).collect(); - traits.sort(); - traits.dedup(); - // FIXME: this could use a better heuristic, like just checking - // that args[1..] is the same. - let all_traits_equal = traits.len() == 1; - - let candidates: Vec<String> = candidates - .into_iter() - .map(|c| { - if all_traits_equal { - format!("\n {}", c.self_ty()) - } else { - format!("\n {c}") - } - }) - .collect(); - - let end = if candidates.len() <= 9 { candidates.len() } else { 8 }; - err.help(format!( - "the following {other}types implement trait `{}`:{}{}", - trait_ref.print_only_trait_path(), - candidates[..end].join(""), - if candidates.len() > 9 { - format!("\nand {} others", candidates.len() - 8) - } else { - String::new() - } - )); - true - }; - - let def_id = trait_ref.def_id(); - if impl_candidates.is_empty() { - if self.tcx.trait_is_auto(def_id) - || self.tcx.lang_items().iter().any(|(_, id)| id == def_id) - || self.tcx.get_diagnostic_name(def_id).is_some() - { - // Mentioning implementers of `Copy`, `Debug` and friends is not useful. - return false; - } - let mut impl_candidates: Vec<_> = self - .tcx - .all_impls(def_id) - // Ignore automatically derived impls and `!Trait` impls. - .filter(|&def_id| { - self.tcx.impl_polarity(def_id) != ty::ImplPolarity::Negative - || self.tcx.is_automatically_derived(def_id) - }) - .filter_map(|def_id| self.tcx.impl_trait_ref(def_id)) - .map(ty::EarlyBinder::instantiate_identity) - .filter(|trait_ref| { - let self_ty = trait_ref.self_ty(); - // Avoid mentioning type parameters. - if let ty::Param(_) = self_ty.kind() { - false - } - // Avoid mentioning types that are private to another crate - else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { - // FIXME(compiler-errors): This could be generalized, both to - // be more granular, and probably look past other `#[fundamental]` - // types, too. - self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) - } else { - true - } - }) - .collect(); - - impl_candidates.sort(); - impl_candidates.dedup(); - return report(impl_candidates, err); - } - - // Sort impl candidates so that ordering is consistent for UI tests. - // because the ordering of `impl_candidates` may not be deterministic: - // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507 - // - // Prefer more similar candidates first, then sort lexicographically - // by their normalized string representation. - let mut impl_candidates: Vec<_> = impl_candidates - .iter() - .cloned() - .map(|mut cand| { - // Fold the consts so that they shows up as, e.g., `10` - // instead of `core::::array::{impl#30}::{constant#0}`. - cand.trait_ref = cand.trait_ref.fold_with(&mut BottomUpFolder { - tcx: self.tcx, - ty_op: |ty| ty, - lt_op: |lt| lt, - ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()), - }); - cand - }) - .collect(); - impl_candidates.sort_by_key(|cand| (cand.similarity, cand.trait_ref)); - let mut impl_candidates: Vec<_> = - impl_candidates.into_iter().map(|cand| cand.trait_ref).collect(); - impl_candidates.dedup(); - - report(impl_candidates, err) - } - - fn report_similar_impl_candidates_for_root_obligation( - &self, - obligation: &PredicateObligation<'tcx>, - trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, - body_def_id: LocalDefId, - err: &mut Diagnostic, - ) { - // This is *almost* equivalent to - // `obligation.cause.code().peel_derives()`, but it gives us the - // trait predicate for that corresponding root obligation. This - // lets us get a derived obligation from a type parameter, like - // when calling `string.strip_suffix(p)` where `p` is *not* an - // implementer of `Pattern<'_>`. - let mut code = obligation.cause.code(); - let mut trait_pred = trait_predicate; - let mut peeled = false; - while let Some((parent_code, parent_trait_pred)) = code.parent() { - code = parent_code; - if let Some(parent_trait_pred) = parent_trait_pred { - trait_pred = parent_trait_pred; - peeled = true; - } - } - let def_id = trait_pred.def_id(); - // Mention *all* the `impl`s for the *top most* obligation, the - // user might have meant to use one of them, if any found. We skip - // auto-traits or fundamental traits that might not be exactly what - // the user might expect to be presented with. Instead this is - // useful for less general traits. - if peeled - && !self.tcx.trait_is_auto(def_id) - && !self.tcx.lang_items().iter().any(|(_, id)| id == def_id) - { - let trait_ref = trait_pred.to_poly_trait_ref(); - let impl_candidates = self.find_similar_impl_candidates(trait_pred); - self.report_similar_impl_candidates( - &impl_candidates, - trait_ref, - body_def_id, - err, - true, - obligation.param_env, - ); - } - } - - /// Gets the parent trait chain start - fn get_parent_trait_ref( - &self, - code: &ObligationCauseCode<'tcx>, - ) -> Option<(String, Option<Span>)> { - match code { - ObligationCauseCode::BuiltinDerivedObligation(data) => { - let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); - match self.get_parent_trait_ref(&data.parent_code) { - Some(t) => Some(t), - None => { - let ty = parent_trait_ref.skip_binder().self_ty(); - let span = TyCategory::from_ty(self.tcx, ty) - .map(|(_, def_id)| self.tcx.def_span(def_id)); - Some((ty.to_string(), span)) - } - } - } - ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => { - self.get_parent_trait_ref(&parent_code) - } - _ => None, - } - } - - /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait - /// with the same path as `trait_ref`, a help message about - /// a probable version mismatch is added to `err` - fn note_version_mismatch( - &self, - err: &mut Diagnostic, - trait_ref: &ty::PolyTraitRef<'tcx>, - ) -> bool { - let get_trait_impls = |trait_def_id| { - let mut trait_impls = vec![]; - self.tcx.for_each_relevant_impl( - trait_def_id, - trait_ref.skip_binder().self_ty(), - |impl_def_id| { - trait_impls.push(impl_def_id); - }, - ); - trait_impls - }; - - let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); - let traits_with_same_path: std::collections::BTreeSet<_> = self - .tcx - .all_traits() - .filter(|trait_def_id| *trait_def_id != trait_ref.def_id()) - .filter(|trait_def_id| self.tcx.def_path_str(*trait_def_id) == required_trait_path) - .collect(); - let mut suggested = false; - for trait_with_same_path in traits_with_same_path { - let trait_impls = get_trait_impls(trait_with_same_path); - if trait_impls.is_empty() { - continue; - } - let impl_spans: Vec<_> = - trait_impls.iter().map(|impl_def_id| self.tcx.def_span(*impl_def_id)).collect(); - err.span_help( - impl_spans, - format!("trait impl{} with same name found", pluralize!(trait_impls.len())), - ); - let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); - let crate_msg = - format!("perhaps two different versions of crate `{trait_crate}` are being used?"); - err.note(crate_msg); - suggested = true; - } - suggested - } - - fn mk_trait_obligation_with_new_self_ty( - &self, - param_env: ty::ParamEnv<'tcx>, - trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, - ) -> PredicateObligation<'tcx> { - let trait_pred = - trait_ref_and_ty.map_bound(|(tr, new_self_ty)| tr.with_self_ty(self.tcx, new_self_ty)); - - Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred) - } - - #[instrument(skip(self), level = "debug")] - fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) { - // Unable to successfully determine, probably means - // insufficient type information, but could mean - // ambiguous impls. The latter *ought* to be a - // coherence violation, so we don't report it here. - - let predicate = self.resolve_vars_if_possible(obligation.predicate); - let span = obligation.cause.span; - - debug!(?predicate, obligation.cause.code = ?obligation.cause.code()); - - // Ambiguity errors are often caused as fallout from earlier errors. - // We ignore them if this `infcx` is tainted in some cases below. - - let bound_predicate = predicate.kind(); - let mut err = match bound_predicate.skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { - let trait_ref = bound_predicate.rebind(data.trait_ref); - debug!(?trait_ref); - - if predicate.references_error() { - return; - } - - // This is kind of a hack: it frequently happens that some earlier - // error prevents types from being fully inferred, and then we get - // a bunch of uninteresting errors saying something like "<generic - // #0> doesn't implement Sized". It may even be true that we - // could just skip over all checks where the self-ty is an - // inference variable, but I was afraid that there might be an - // inference variable created, registered as an obligation, and - // then never forced by writeback, and hence by skipping here we'd - // be ignoring the fact that we don't KNOW the type works - // out. Though even that would probably be harmless, given that - // we're only talking about builtin traits, which are known to be - // inhabited. We used to check for `self.tcx.sess.has_errors()` to - // avoid inundating the user with unnecessary errors, but we now - // check upstream for type errors and don't add the obligations to - // begin with in those cases. - if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { - if let None = self.tainted_by_errors() { - self.emit_inference_failure_err( - obligation.cause.body_id, - span, - trait_ref.self_ty().skip_binder().into(), - ErrorCode::E0282, - false, - ) - .emit(); - } - return; - } - - // Typically, this ambiguity should only happen if - // there are unresolved type inference variables - // (otherwise it would suggest a coherence - // failure). But given #21974 that is not necessarily - // the case -- we can have multiple where clauses that - // are only distinguished by a region, which results - // in an ambiguity even when all types are fully - // known, since we don't dispatch based on region - // relationships. - - // Pick the first substitution that still contains inference variables as the one - // we're going to emit an error for. If there are none (see above), fall back to - // a more general error. - let subst = data.trait_ref.args.iter().find(|s| s.has_non_region_infer()); - - let mut err = if let Some(subst) = subst { - self.emit_inference_failure_err( - obligation.cause.body_id, - span, - subst, - ErrorCode::E0283, - true, - ) - } else { - struct_span_err!( - self.tcx.sess, - span, - E0283, - "type annotations needed: cannot satisfy `{}`", - predicate, - ) - }; - - let ambiguities = ambiguity::recompute_applicable_impls( - self.infcx, - &obligation.with(self.tcx, trait_ref), - ); - let has_non_region_infer = - trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer()); - // It doesn't make sense to talk about applicable impls if there are more - // than a handful of them. - if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { - if self.tainted_by_errors().is_some() && subst.is_none() { - // If `subst.is_none()`, then this is probably two param-env - // candidates or impl candidates that are equal modulo lifetimes. - // Therefore, if we've already emitted an error, just skip this - // one, since it's not particularly actionable. - err.cancel(); - return; - } - self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate); - } else { - if self.tainted_by_errors().is_some() { - err.cancel(); - return; - } - err.note(format!("cannot satisfy `{predicate}`")); - let impl_candidates = self - .find_similar_impl_candidates(predicate.to_opt_poly_trait_pred().unwrap()); - if impl_candidates.len() < 10 { - self.report_similar_impl_candidates( - impl_candidates.as_slice(), - trait_ref, - obligation.cause.body_id, - &mut err, - false, - obligation.param_env, - ); - } - } - - if let ObligationCauseCode::ItemObligation(def_id) - | ObligationCauseCode::ExprItemObligation(def_id, ..) = *obligation.cause.code() - { - self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); - } - - if let Some(ty::GenericArgKind::Type(_)) = subst.map(|subst| subst.unpack()) - && let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) - { - let mut expr_finder = FindExprBySpan::new(span); - expr_finder.visit_expr(&self.tcx.hir().body(body_id).value); - - if let Some(hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), .. } - ) = expr_finder.result - && let [ - .., - trait_path_segment @ hir::PathSegment { - res: rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, trait_id), - .. - }, - hir::PathSegment { - ident: assoc_item_name, - res: rustc_hir::def::Res::Def(_, item_id), - .. - } - ] = path.segments - && data.trait_ref.def_id == *trait_id - && self.tcx.trait_of_item(*item_id) == Some(*trait_id) - && let None = self.tainted_by_errors() - { - let (verb, noun) = match self.tcx.associated_item(item_id).kind { - ty::AssocKind::Const => ("refer to the", "constant"), - ty::AssocKind::Fn => ("call", "function"), - ty::AssocKind::Type => ("refer to the", "type"), // this is already covered by E0223, but this single match arm doesn't hurt here - }; - - // Replace the more general E0283 with a more specific error - err.cancel(); - err = self.tcx.sess.struct_span_err_with_code( - span, - format!( - "cannot {verb} associated {noun} on trait without specifying the corresponding `impl` type", - ), - rustc_errors::error_code!(E0790), - ); - - if let Some(local_def_id) = data.trait_ref.def_id.as_local() - && let Some(hir::Node::Item(hir::Item { ident: trait_name, kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), .. })) = self.tcx.hir().find_by_def_id(local_def_id) - && let Some(method_ref) = trait_item_refs.iter().find(|item_ref| item_ref.ident == *assoc_item_name) { - err.span_label(method_ref.span, format!("`{trait_name}::{assoc_item_name}` defined here")); - } - - err.span_label(span, format!("cannot {verb} associated {noun} of trait")); - - let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id); - - if trait_impls.blanket_impls().is_empty() - && let Some(impl_def_id) = trait_impls.non_blanket_impls().values().flatten().next() - { - let non_blanket_impl_count = trait_impls.non_blanket_impls().values().flatten().count(); - // If there is only one implementation of the trait, suggest using it. - // Otherwise, use a placeholder comment for the implementation. - let (message, impl_suggestion) = if non_blanket_impl_count == 1 {( - "use the fully-qualified path to the only available implementation", - format!("<{} as ", self.tcx.type_of(impl_def_id).instantiate_identity()) - )} else { - ("use a fully-qualified path to a specific available implementation", - "</* self type */ as ".to_string() - )}; - let mut suggestions = vec![( - path.span.shrink_to_lo(), - impl_suggestion - )]; - if let Some(generic_arg) = trait_path_segment.args { - let between_span = trait_path_segment.ident.span.between(generic_arg.span_ext); - // get rid of :: between Trait and <type> - // must be '::' between them, otherwise the parser won't accept the code - suggestions.push((between_span, "".to_string(),)); - suggestions.push((generic_arg.span_ext.shrink_to_hi(), ">".to_string())); - } else { - suggestions.push((trait_path_segment.ident.span.shrink_to_hi(), ">".to_string())); - } - err.multipart_suggestion( - message, - suggestions, - Applicability::MaybeIncorrect - ); - } - } - }; - - err - } - - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - // Same hacky approach as above to avoid deluging user - // with error messages. - if arg.references_error() - || self.tcx.sess.has_errors().is_some() - || self.tainted_by_errors().is_some() - { - return; - } - - self.emit_inference_failure_err( - obligation.cause.body_id, - span, - arg, - ErrorCode::E0282, - false, - ) - } - - ty::PredicateKind::Subtype(data) => { - if data.references_error() - || self.tcx.sess.has_errors().is_some() - || self.tainted_by_errors().is_some() - { - // no need to overload user in such cases - return; - } - let SubtypePredicate { a_is_expected: _, a, b } = data; - // both must be type variables, or the other would've been instantiated - assert!(a.is_ty_var() && b.is_ty_var()); - self.emit_inference_failure_err( - obligation.cause.body_id, - span, - a.into(), - ErrorCode::E0282, - true, - ) - } - ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { - if predicate.references_error() || self.tainted_by_errors().is_some() { - return; - } - let subst = data - .projection_ty - .args - .iter() - .chain(Some(data.term.into_arg())) - .find(|g| g.has_non_region_infer()); - if let Some(subst) = subst { - let mut err = self.emit_inference_failure_err( - obligation.cause.body_id, - span, - subst, - ErrorCode::E0284, - true, - ); - err.note(format!("cannot satisfy `{predicate}`")); - err - } else { - // If we can't find a substitution, just print a generic error - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, - ); - err.span_label(span, format!("cannot satisfy `{predicate}`")); - err - } - } - - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => { - if predicate.references_error() || self.tainted_by_errors().is_some() { - return; - } - let subst = data.walk().find(|g| g.is_non_region_infer()); - if let Some(subst) = subst { - let err = self.emit_inference_failure_err( - obligation.cause.body_id, - span, - subst, - ErrorCode::E0284, - true, - ); - err - } else { - // If we can't find a substitution, just print a generic error - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, - ); - err.span_label(span, format!("cannot satisfy `{predicate}`")); - err - } - } - _ => { - if self.tcx.sess.has_errors().is_some() || self.tainted_by_errors().is_some() { - return; - } - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, - ); - err.span_label(span, format!("cannot satisfy `{predicate}`")); - err - } - }; - self.note_obligation_cause(&mut err, obligation); - err.emit(); - } - - fn annotate_source_of_ambiguity( - &self, - err: &mut Diagnostic, - ambiguities: &[ambiguity::Ambiguity], - predicate: ty::Predicate<'tcx>, - ) { - let mut spans = vec![]; - let mut crates = vec![]; - let mut post = vec![]; - let mut has_param_env = false; - for ambiguity in ambiguities { - match ambiguity { - ambiguity::Ambiguity::DefId(impl_def_id) => { - match self.tcx.span_of_impl(*impl_def_id) { - Ok(span) => spans.push(span), - Err(name) => { - crates.push(name); - if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) { - post.push(header); - } - } - } - } - ambiguity::Ambiguity::ParamEnv(span) => { - has_param_env = true; - spans.push(*span); - } - } - } - let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect(); - crate_names.sort(); - crate_names.dedup(); - post.sort(); - post.dedup(); - - if self.tainted_by_errors().is_some() - && (crate_names.len() == 1 - && spans.len() == 0 - && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str()) - || predicate.visit_with(&mut HasNumericInferVisitor).is_break()) - { - // Avoid complaining about other inference issues for expressions like - // `42 >> 1`, where the types are still `{integer}`, but we want to - // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too? - // NOTE(eddyb) this was `.cancel()`, but `err` - // is borrowed, so we can't fully defuse it. - err.downgrade_to_delayed_bug(); - return; - } - - let msg = format!( - "multiple `impl`s{} satisfying `{}` found", - if has_param_env { " or `where` clauses" } else { "" }, - predicate - ); - let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { - format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),) - } else if post.len() == 1 { - format!(": `{}`", post[0]) - } else { - String::new() - }; - - match (spans.len(), crates.len(), crate_names.len()) { - (0, 0, 0) => { - err.note(format!("cannot satisfy `{predicate}`")); - } - (0, _, 1) => { - err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,)); - } - (0, _, _) => { - err.note(format!( - "{} in the following crates: {}{}", - msg, - crate_names.join(", "), - post, - )); - } - (_, 0, 0) => { - let span: MultiSpan = spans.into(); - err.span_note(span, msg); - } - (_, 1, 1) => { - let span: MultiSpan = spans.into(); - err.span_note(span, msg); - err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,)); - } - _ => { - let span: MultiSpan = spans.into(); - err.span_note(span, msg); - err.note(format!( - "and more `impl`s found in the following crates: {}{}", - crate_names.join(", "), - post, - )); - } - } - } - - /// Returns `true` if the trait predicate may apply for *some* assignment - /// to the type parameters. - fn predicate_can_apply( - &self, - param_env: ty::ParamEnv<'tcx>, - pred: ty::PolyTraitPredicate<'tcx>, - ) -> bool { - struct ParamToVarFolder<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, - var_map: FxHashMap<Ty<'tcx>, Ty<'tcx>>, - } - - impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ParamToVarFolder<'a, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Param(_) = *ty.kind() { - let infcx = self.infcx; - *self.var_map.entry(ty).or_insert_with(|| { - infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: DUMMY_SP, - }) - }) - } else { - ty.super_fold_with(self) - } - } - } - - self.probe(|_| { - let cleaned_pred = - pred.fold_with(&mut ParamToVarFolder { infcx: self, var_map: Default::default() }); - - let InferOk { value: cleaned_pred, .. } = - self.infcx.at(&ObligationCause::dummy(), param_env).normalize(cleaned_pred); - - let obligation = - Obligation::new(self.tcx, ObligationCause::dummy(), param_env, cleaned_pred); - - self.predicate_may_hold(&obligation) - }) - } - - fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>) { - // First, attempt to add note to this error with an async-await-specific - // message, and fall back to regular note otherwise. - if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { - self.note_obligation_cause_code( - obligation.cause.body_id, - err, - obligation.predicate, - obligation.param_env, - obligation.cause.code(), - &mut vec![], - &mut Default::default(), - ); - self.suggest_unsized_bound_if_applicable(err, obligation); - } - } - - #[instrument(level = "debug", skip_all)] - fn suggest_unsized_bound_if_applicable( - &self, - err: &mut Diagnostic, - obligation: &PredicateObligation<'tcx>, - ) { - let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = - obligation.predicate.kind().skip_binder() - else { - return; - }; - let (ObligationCauseCode::BindingObligation(item_def_id, span) - | ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..)) = - *obligation.cause.code().peel_derives() - else { - return; - }; - debug!(?pred, ?item_def_id, ?span); - - let (Some(node), true) = ( - self.tcx.hir().get_if_local(item_def_id), - Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), - ) else { - return; - }; - self.maybe_suggest_unsized_generics(err, span, node); - } - - #[instrument(level = "debug", skip_all)] - fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>) { - let Some(generics) = node.generics() else { - return; - }; - let sized_trait = self.tcx.lang_items().sized_trait(); - debug!(?generics.params); - debug!(?generics.predicates); - let Some(param) = generics.params.iter().find(|param| param.span == span) else { - return; - }; - // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit - // `Sized` bound is there intentionally and we don't need to suggest relaxing it. - let explicitly_sized = generics - .bounds_for_param(param.def_id) - .flat_map(|bp| bp.bounds) - .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait); - if explicitly_sized { - return; - } - debug!(?param); - match node { - hir::Node::Item( - item @ hir::Item { - // Only suggest indirection for uses of type parameters in ADTs. - kind: - hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..), - .. - }, - ) => { - if self.maybe_indirection_for_unsized(err, item, param) { - return; - } - } - _ => {} - }; - // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`. - let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param.def_id) - { - (s, " +") - } else { - (span.shrink_to_hi(), ":") - }; - err.span_suggestion_verbose( - span, - "consider relaxing the implicit `Sized` restriction", - format!("{separator} ?Sized"), - Applicability::MachineApplicable, - ); - } - - fn maybe_indirection_for_unsized( - &self, - err: &mut Diagnostic, - item: &Item<'tcx>, - param: &GenericParam<'tcx>, - ) -> bool { - // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a - // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);` - // is not. Look for invalid "bare" parameter uses, and suggest using indirection. - let mut visitor = - FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false }; - visitor.visit_item(item); - if visitor.invalid_spans.is_empty() { - return false; - } - let mut multispan: MultiSpan = param.span.into(); - multispan.push_span_label( - param.span, - format!("this could be changed to `{}: ?Sized`...", param.name.ident()), - ); - for sp in visitor.invalid_spans { - multispan.push_span_label( - sp, - format!("...if indirection were used here: `Box<{}>`", param.name.ident()), - ); - } - err.span_help( - multispan, - format!( - "you could relax the implicit `Sized` bound on `{T}` if it were \ - used through indirection like `&{T}` or `Box<{T}>`", - T = param.name.ident(), - ), - ); - true - } - - fn is_recursive_obligation( - &self, - obligated_types: &mut Vec<Ty<'tcx>>, - cause_code: &ObligationCauseCode<'tcx>, - ) -> bool { - if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code { - let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); - let self_ty = parent_trait_ref.skip_binder().self_ty(); - if obligated_types.iter().any(|ot| ot == &self_ty) { - return true; - } - if let ty::Adt(def, args) = self_ty.kind() - && let [arg] = &args[..] - && let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Adt(inner_def, _) = ty.kind() - && inner_def == def - { - return true; - } - } - false - } - - fn get_standard_error_message( - &self, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, - message: Option<String>, - predicate_is_const: bool, - append_const_msg: Option<AppendConstMessage>, - post_message: String, - ) -> String { - message - .and_then(|cannot_do_this| { - match (predicate_is_const, append_const_msg) { - // do nothing if predicate is not const - (false, _) => Some(cannot_do_this), - // suggested using default post message - (true, Some(AppendConstMessage::Default)) => { - Some(format!("{cannot_do_this} in const contexts")) - } - // overridden post message - (true, Some(AppendConstMessage::Custom(custom_msg))) => { - Some(format!("{cannot_do_this}{custom_msg}")) - } - // fallback to generic message - (true, None) => None, - } - }) - .unwrap_or_else(|| { - format!("the trait bound `{trait_predicate}` is not satisfied{post_message}") - }) - } - - fn get_safe_transmute_error_and_reason( - &self, - obligation: PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - span: Span, - ) -> GetSafeTransmuteErrorAndReason { - use rustc_transmute::Answer; - - // Erase regions because layout code doesn't particularly care about regions. - let trait_ref = self.tcx.erase_regions(self.tcx.erase_late_bound_regions(trait_ref)); - - let src_and_dst = rustc_transmute::Types { - dst: trait_ref.args.type_at(0), - src: trait_ref.args.type_at(1), - }; - let scope = trait_ref.args.type_at(2); - let Some(assume) = rustc_transmute::Assume::from_const( - self.infcx.tcx, - obligation.param_env, - trait_ref.args.const_at(3), - ) else { - span_bug!( - span, - "Unable to construct rustc_transmute::Assume where it was previously possible" - ); - }; - - match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( - obligation.cause, - src_and_dst, - scope, - assume, - ) { - Answer::No(reason) => { - let dst = trait_ref.args.type_at(0); - let src = trait_ref.args.type_at(1); - let err_msg = format!( - "`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`" - ); - let safe_transmute_explanation = match reason { - rustc_transmute::Reason::SrcIsUnspecified => { - format!("`{src}` does not have a well-specified layout") - } - - rustc_transmute::Reason::DstIsUnspecified => { - format!("`{dst}` does not have a well-specified layout") - } - - rustc_transmute::Reason::DstIsBitIncompatible => { - format!("At least one value of `{src}` isn't a bit-valid value of `{dst}`") - } - - rustc_transmute::Reason::DstIsPrivate => format!( - "`{dst}` is or contains a type or field that is not visible in that scope" - ), - rustc_transmute::Reason::DstIsTooBig => { - format!("The size of `{src}` is smaller than the size of `{dst}`") - } - rustc_transmute::Reason::SrcSizeOverflow => { - format!( - "values of the type `{src}` are too big for the current architecture" - ) - } - rustc_transmute::Reason::DstSizeOverflow => { - format!( - "values of the type `{dst}` are too big for the current architecture" - ) - } - rustc_transmute::Reason::DstHasStricterAlignment { - src_min_align, - dst_min_align, - } => { - format!( - "The minimum alignment of `{src}` ({src_min_align}) should be greater than that of `{dst}` ({dst_min_align})" - ) - } - rustc_transmute::Reason::DstIsMoreUnique => { - format!("`{src}` is a shared reference, but `{dst}` is a unique reference") - } - // Already reported by rustc - rustc_transmute::Reason::TypeError => { - return GetSafeTransmuteErrorAndReason::Silent; - } - rustc_transmute::Reason::SrcLayoutUnknown => { - format!("`{src}` has an unknown layout") - } - rustc_transmute::Reason::DstLayoutUnknown => { - format!("`{dst}` has an unknown layout") - } - }; - GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation } - } - // Should never get a Yes at this point! We already ran it before, and did not get a Yes. - Answer::Yes => span_bug!( - span, - "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes", - ), - other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"), - } - } - - fn add_tuple_trait_message( - &self, - obligation_cause_code: &ObligationCauseCode<'tcx>, - err: &mut Diagnostic, - ) { - match obligation_cause_code { - ObligationCauseCode::RustCall => { - err.set_primary_message("functions with the \"rust-call\" ABI must take a single non-self tuple argument"); - } - ObligationCauseCode::BindingObligation(def_id, _) - | ObligationCauseCode::ItemObligation(def_id) - if self.tcx.is_fn_trait(*def_id) => - { - err.code(rustc_errors::error_code!(E0059)); - err.set_primary_message(format!( - "type parameter to bare `{}` trait must be a tuple", - self.tcx.def_path_str(*def_id) - )); - } - _ => {} - } - } - - fn try_to_add_help_message( - &self, - obligation: &PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, - err: &mut Diagnostic, - span: Span, - is_fn_trait: bool, - suggested: bool, - unsatisfied_const: bool, - ) { - let body_def_id = obligation.cause.body_id; - let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } = - obligation.cause.code() - { - *rhs_span - } else { - span - }; - - // Try to report a help message - if is_fn_trait - && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( - obligation.param_env, - trait_ref.self_ty(), - trait_predicate.skip_binder().polarity, - ) - { - self.add_help_message_for_fn_trait(trait_ref, err, implemented_kind, params); - } else if !trait_ref.has_non_region_infer() - && self.predicate_can_apply(obligation.param_env, *trait_predicate) - { - // If a where-clause may be useful, remind the - // user that they can add it. - // - // don't display an on-unimplemented note, as - // these notes will often be of the form - // "the type `T` can't be frobnicated" - // which is somewhat confusing. - self.suggest_restricting_param_bound( - err, - *trait_predicate, - None, - obligation.cause.body_id, - ); - } else if trait_ref.def_id().is_local() - && self.tcx.trait_impls_of(trait_ref.def_id()).is_empty() - && !self.tcx.trait_is_auto(trait_ref.def_id()) - && !self.tcx.trait_is_alias(trait_ref.def_id()) - { - err.span_help( - self.tcx.def_span(trait_ref.def_id()), - crate::fluent_generated::trait_selection_trait_has_no_impls, - ); - } else if !suggested && !unsatisfied_const { - // Can't show anything else useful, try to find similar impls. - let impl_candidates = self.find_similar_impl_candidates(*trait_predicate); - if !self.report_similar_impl_candidates( - &impl_candidates, - trait_ref, - body_def_id, - err, - true, - obligation.param_env, - ) { - self.report_similar_impl_candidates_for_root_obligation( - &obligation, - *trait_predicate, - body_def_id, - err, - ); - } - - self.suggest_convert_to_slice( - err, - obligation, - trait_ref, - impl_candidates.as_slice(), - span, - ); - } - } - - fn add_help_message_for_fn_trait( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - err: &mut Diagnostic, - implemented_kind: ty::ClosureKind, - params: ty::Binder<'tcx, Ty<'tcx>>, - ) { - // If the type implements `Fn`, `FnMut`, or `FnOnce`, suppress the following - // suggestion to add trait bounds for the type, since we only typically implement - // these traits once. - - // Note if the `FnMut` or `FnOnce` is less general than the trait we're trying - // to implement. - let selected_kind = self - .tcx - .fn_trait_kind_from_def_id(trait_ref.def_id()) - .expect("expected to map DefId to ClosureKind"); - if !implemented_kind.extends(selected_kind) { - err.note(format!( - "`{}` implements `{}`, but it must implement `{}`, which is more general", - trait_ref.skip_binder().self_ty(), - implemented_kind, - selected_kind - )); - } - - // Note any argument mismatches - let given_ty = params.skip_binder(); - let expected_ty = trait_ref.skip_binder().args.type_at(1); - if let ty::Tuple(given) = given_ty.kind() - && let ty::Tuple(expected) = expected_ty.kind() - { - if expected.len() != given.len() { - // Note number of types that were expected and given - err.note( - format!( - "expected a closure taking {} argument{}, but one taking {} argument{} was given", - given.len(), - pluralize!(given.len()), - expected.len(), - pluralize!(expected.len()), - ) - ); - } else if !self.same_type_modulo_infer(given_ty, expected_ty) { - // Print type mismatch - let (expected_args, given_args) = - self.cmp(given_ty, expected_ty); - err.note_expected_found( - &"a closure with arguments", - expected_args, - &"a closure with arguments", - given_args, - ); - } - } - } - - fn maybe_add_note_for_unsatisfied_const( - &self, - _obligation: &PredicateObligation<'tcx>, - _trait_ref: ty::PolyTraitRef<'tcx>, - _trait_predicate: &ty::PolyTraitPredicate<'tcx>, - _err: &mut Diagnostic, - _span: Span, - ) -> UnsatisfiedConst { - let unsatisfied_const = UnsatisfiedConst(false); - // FIXME(effects) - unsatisfied_const - } - - fn report_closure_error( - &self, - obligation: &PredicateObligation<'tcx>, - closure_def_id: DefId, - found_kind: ty::ClosureKind, - kind: ty::ClosureKind, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let closure_span = self.tcx.def_span(closure_def_id); - - let mut err = ClosureKindMismatch { - closure_span, - expected: kind, - found: found_kind, - cause_span: obligation.cause.span, - fn_once_label: None, - fn_mut_label: None, - }; - - // Additional context information explaining why the closure only implements - // a particular trait. - if let Some(typeck_results) = &self.typeck_results { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); - match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) { - (ty::ClosureKind::FnOnce, Some((span, place))) => { - err.fn_once_label = Some(ClosureFnOnceLabel { - span: *span, - place: ty::place_to_string_for_capture(self.tcx, &place), - }) - } - (ty::ClosureKind::FnMut, Some((span, place))) => { - err.fn_mut_label = Some(ClosureFnMutLabel { - span: *span, - place: ty::place_to_string_for_capture(self.tcx, &place), - }) - } - _ => {} - } - } - - self.tcx.sess.create_err(err) - } - - fn report_type_parameter_mismatch_cyclic_type_error( - &self, - obligation: &PredicateObligation<'tcx>, - found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - terr: TypeError<'tcx>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let self_ty = found_trait_ref.self_ty().skip_binder(); - let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() { - ( - ObligationCause::dummy_with_span(self.tcx.def_span(def_id)), - TypeError::CyclicTy(self_ty), - ) - } else { - (obligation.cause.clone(), terr) - }; - self.report_and_explain_type_error( - TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref), - terr, - ) - } - - fn report_opaque_type_auto_trait_leakage( - &self, - obligation: &PredicateObligation<'tcx>, - def_id: DefId, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let name = match self.tcx.opaque_type_origin(def_id.expect_local()) { - hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { - "opaque type".to_string() - } - hir::OpaqueTyOrigin::TyAlias { .. } => { - format!("`{}`", self.tcx.def_path_debug_str(def_id)) - } - }; - let mut err = self.tcx.sess.struct_span_err( - obligation.cause.span, - format!("cannot check whether the hidden type of {name} satisfies auto traits"), - ); - err.span_note(self.tcx.def_span(def_id), "opaque type is declared here"); - match self.defining_use_anchor { - DefiningAnchor::Bubble | DefiningAnchor::Error => {} - DefiningAnchor::Bind(bind) => { - err.span_note( - self.tcx.def_ident_span(bind).unwrap_or_else(|| self.tcx.def_span(bind)), - "this item depends on auto traits of the hidden type, \ - but may also be registering the hidden type. \ - This is not supported right now. \ - You can try moving the opaque type and the item that actually registers a hidden type into a new submodule".to_string(), - ); - } - }; - err - } - - fn report_type_parameter_mismatch_error( - &self, - obligation: &PredicateObligation<'tcx>, - span: Span, - found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref); - let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref); - - if expected_trait_ref.self_ty().references_error() { - return None; - } - - let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else { - return None; - }; - - let found_did = match *found_trait_ty.kind() { - ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) | ty::Generator(did, ..) => { - Some(did) - } - ty::Adt(def, _) => Some(def.did()), - _ => None, - }; - - let found_node = found_did.and_then(|did| self.tcx.hir().get_if_local(did)); - let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did)); - - if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { - // We check closures twice, with obligations flowing in different directions, - // but we want to complain about them only once. - return None; - } - - self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); - - let mut not_tupled = false; - - let found = match found_trait_ref.skip_binder().args.type_at(1).kind() { - ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], - _ => { - not_tupled = true; - vec![ArgKind::empty()] - } - }; - - let expected_ty = expected_trait_ref.skip_binder().args.type_at(1); - let expected = match expected_ty.kind() { - ty::Tuple(ref tys) => { - tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect() - } - _ => { - not_tupled = true; - vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())] - } - }; - - // If this is a `Fn` family trait and either the expected or found - // is not tupled, then fall back to just a regular mismatch error. - // This shouldn't be common unless manually implementing one of the - // traits manually, but don't make it more confusing when it does - // happen. - Some( - if Some(expected_trait_ref.def_id()) != self.tcx.lang_items().gen_trait() && not_tupled - { - self.report_and_explain_type_error( - TypeTrace::poly_trait_refs( - &obligation.cause, - true, - expected_trait_ref, - found_trait_ref, - ), - ty::error::TypeError::Mismatch, - ) - } else if found.len() == expected.len() { - self.report_closure_arg_mismatch( - span, - found_span, - found_trait_ref, - expected_trait_ref, - obligation.cause.code(), - found_node, - obligation.param_env, - ) - } else { - let (closure_span, closure_arg_span, found) = found_did - .and_then(|did| { - let node = self.tcx.hir().get_if_local(did)?; - let (found_span, closure_arg_span, found) = - self.get_fn_like_arguments(node)?; - Some((Some(found_span), closure_arg_span, found)) - }) - .unwrap_or((found_span, None, found)); - - self.report_arg_count_mismatch( - span, - closure_span, - expected, - found, - found_trait_ty.is_closure(), - closure_arg_span, - ) - }, - ) - } - - fn report_not_const_evaluatable_error( - &self, - obligation: &PredicateObligation<'tcx>, - span: Span, - ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - if !self.tcx.features().generic_const_exprs { - let mut err = self - .tcx - .sess - .struct_span_err(span, "constant expression depends on a generic parameter"); - // FIXME(const_generics): we should suggest to the user how they can resolve this - // issue. However, this is currently not actually possible - // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). - // - // Note that with `feature(generic_const_exprs)` this case should not - // be reachable. - err.note("this may fail depending on what value the parameter takes"); - err.emit(); - return None; - } - - match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { - let ty::ConstKind::Unevaluated(uv) = ct.kind() else { - bug!("const evaluatable failed for non-unevaluated const `{ct:?}`"); - }; - let mut err = self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); - let const_span = self.tcx.def_span(uv.def); - match self.tcx.sess.source_map().span_to_snippet(const_span) { - Ok(snippet) => err.help(format!( - "try adding a `where` bound using this expression: `where [(); {snippet}]:`" - )), - _ => err.help("consider adding a `where` bound using this expression"), - }; - Some(err) - } - _ => { - span_bug!( - span, - "unexpected non-ConstEvaluatable predicate, this should not be reachable" - ) - } - } - } -} - struct UnsatisfiedConst(pub bool); -fn get_explanation_based_on_obligation<'tcx>( - obligation: &PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, - pre_message: String, -) -> String { - if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { - "consider using `()`, or a `Result`".to_owned() - } else { - let ty_desc = match trait_ref.skip_binder().self_ty().kind() { - ty::FnDef(_, _) => Some("fn item"), - ty::Closure(_, _) => Some("closure"), - _ => None, - }; - - match ty_desc { - Some(desc) => format!( - "{}the trait `{}` is not implemented for {} `{}`", - pre_message, - trait_predicate.print_modifiers_and_trait_path(), - desc, - trait_ref.skip_binder().self_ty(), - ), - None => format!( - "{}the trait `{}` is not implemented for `{}`", - pre_message, - trait_predicate.print_modifiers_and_trait_path(), - trait_ref.skip_binder().self_ty(), - ), - } - } -} /// Crude way of getting back an `Expr` from a `Span`. pub struct FindExprBySpan<'hir> { pub span: Span, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index d645dc033b8..d9059e46a8c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -18,7 +18,7 @@ use crate::errors::{ EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, }; -use super::InferCtxtPrivExt; +use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; pub trait TypeErrCtxtExt<'tcx> { /*private*/ diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index b7c73501280..9aebe77a104 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -41,8 +41,8 @@ use rustc_target::spec::abi; use std::borrow::Cow; use std::iter; -use super::InferCtxtPrivExt; use crate::infer::InferCtxtExt as _; +use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; @@ -4254,6 +4254,39 @@ impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { } } +pub(super) fn get_explanation_based_on_obligation<'tcx>( + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + pre_message: String, +) -> String { + if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { + "consider using `()`, or a `Result`".to_owned() + } else { + let ty_desc = match trait_ref.skip_binder().self_ty().kind() { + ty::FnDef(_, _) => Some("fn item"), + ty::Closure(_, _) => Some("closure"), + _ => None, + }; + + match ty_desc { + Some(desc) => format!( + "{}the trait `{}` is not implemented for {} `{}`", + pre_message, + trait_predicate.print_modifiers_and_trait_path(), + desc, + trait_ref.skip_binder().self_ty(), + ), + None => format!( + "{}the trait `{}` is not implemented for `{}`", + pre_message, + trait_predicate.print_modifiers_and_trait_path(), + trait_ref.skip_binder().self_ty(), + ), + } + } +} + // Replace `param` with `replace_ty` struct ReplaceImplTraitFolder<'tcx> { tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs new file mode 100644 index 00000000000..8adfb27a3f4 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -0,0 +1,3241 @@ +use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _}; +use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _}; +use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch}; +use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::{self, InferCtxt}; +use crate::traits::error_reporting::infer_ctxt_ext::InferCtxtExt; +use crate::traits::error_reporting::{ambiguity, ambiguity::Ambiguity::*}; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::specialize::to_pretty_impl_header; +use crate::traits::NormalizeExt; +use crate::traits::{ + elaborate, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, + ObligationCause, ObligationCauseCode, ObligationCtxt, OutputTypeParameterMismatch, Overflow, + PredicateObligation, SelectionError, TraitNotObjectSafe, +}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_errors::{ + pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + MultiSpan, Style, +}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{GenericParam, Item, Node}; +use rustc_infer::infer::error_reporting::TypeErrCtxt; +use rustc_infer::infer::{InferOk, TypeTrace}; +use rustc_middle::traits::select::OverflowError; +use rustc_middle::traits::{DefiningAnchor, SelectionOutputTypeParameterMismatch}; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print}; +use rustc_middle::ty::{ + self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, + TypeVisitable, TypeVisitableExt, +}; +use rustc_session::config::{DumpSolverProofTree, TraitSolver}; +use rustc_session::Limit; +use rustc_span::def_id::LOCAL_CRATE; +use rustc_span::symbol::sym; +use rustc_span::{ExpnKind, Span, DUMMY_SP}; +use std::borrow::Cow; +use std::fmt; +use std::iter; + +use super::{ + dump_proof_tree, ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam, + GetSafeTransmuteErrorAndReason, HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst, +}; + +pub use rustc_infer::traits::error_reporting::*; + +pub trait TypeErrCtxtExt<'tcx> { + fn build_overflow_error<T>( + &self, + predicate: &T, + span: Span, + suggest_increasing_limit: bool, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> + where + T: fmt::Display + + TypeFoldable<TyCtxt<'tcx>> + + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, + <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug; + + fn report_overflow_error<T>( + &self, + predicate: &T, + span: Span, + suggest_increasing_limit: bool, + mutate: impl FnOnce(&mut Diagnostic), + ) -> ! + where + T: fmt::Display + + TypeFoldable<TyCtxt<'tcx>> + + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, + <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug; + + fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed; + + fn report_fulfillment_errors(&self, errors: Vec<FulfillmentError<'tcx>>) -> ErrorGuaranteed; + + fn report_overflow_obligation<T>( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: ToPredicate<'tcx> + Clone; + + fn suggest_new_overflow_limit(&self, err: &mut Diagnostic); + + fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; + + /// The `root_obligation` parameter should be the `root_obligation` field + /// from a `FulfillmentError`. If no `FulfillmentError` is available, + /// then it should be the same as `obligation`. + fn report_selection_error( + &self, + obligation: PredicateObligation<'tcx>, + root_obligation: &PredicateObligation<'tcx>, + error: &SelectionError<'tcx>, + ); + + fn report_const_param_not_wf( + &self, + ty: Ty<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; +} + +impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { + fn report_fulfillment_errors( + &self, + mut errors: Vec<FulfillmentError<'tcx>>, + ) -> ErrorGuaranteed { + #[derive(Debug)] + struct ErrorDescriptor<'tcx> { + predicate: ty::Predicate<'tcx>, + index: Option<usize>, // None if this is an old error + } + + let mut error_map: FxIndexMap<_, Vec<_>> = self + .reported_trait_errors + .borrow() + .iter() + .map(|(&span, predicates)| { + ( + span, + predicates + .iter() + .map(|&predicate| ErrorDescriptor { predicate, index: None }) + .collect(), + ) + }) + .collect(); + + // Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics + // with more relevant type information and hide redundant E0282 errors. + errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) + if Some(pred.def_id()) == self.tcx.lang_items().sized_trait() => + { + 1 + } + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3, + ty::PredicateKind::Coerce(_) => 2, + _ => 0, + }); + + for (index, error) in errors.iter().enumerate() { + // We want to ignore desugarings here: spans are equivalent even + // if one is the result of a desugaring and the other is not. + let mut span = error.obligation.cause.span; + let expn_data = span.ctxt().outer_expn_data(); + if let ExpnKind::Desugaring(_) = expn_data.kind { + span = expn_data.call_site; + } + + error_map.entry(span).or_default().push(ErrorDescriptor { + predicate: error.obligation.predicate, + index: Some(index), + }); + + self.reported_trait_errors + .borrow_mut() + .entry(span) + .or_default() + .push(error.obligation.predicate); + } + + // We do this in 2 passes because we want to display errors in order, though + // maybe it *is* better to sort errors by span or something. + let mut is_suppressed = vec![false; errors.len()]; + for (_, error_set) in error_map.iter() { + // We want to suppress "duplicate" errors with the same span. + for error in error_set { + if let Some(index) = error.index { + // Suppress errors that are either: + // 1) strictly implied by another error. + // 2) implied by an error with a smaller index. + for error2 in error_set { + if error2.index.is_some_and(|index2| is_suppressed[index2]) { + // Avoid errors being suppressed by already-suppressed + // errors, to prevent all errors from being suppressed + // at once. + continue; + } + + if self.error_implies(error2.predicate, error.predicate) + && !(error2.index >= error.index + && self.error_implies(error.predicate, error2.predicate)) + { + info!("skipping {:?} (implied by {:?})", error, error2); + is_suppressed[index] = true; + break; + } + } + } + } + } + + for from_expansion in [false, true] { + for (error, suppressed) in iter::zip(&errors, &is_suppressed) { + if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion { + self.report_fulfillment_error(error); + } + } + } + + self.tcx.sess.delay_span_bug(DUMMY_SP, "expected fulfillment errors") + } + + /// Reports that an overflow has occurred and halts compilation. We + /// halt compilation unconditionally because it is important that + /// overflows never be masked -- they basically represent computations + /// whose result could not be truly determined and thus we can't say + /// if the program type checks or not -- and they are unusual + /// occurrences in any case. + fn report_overflow_error<T>( + &self, + predicate: &T, + span: Span, + suggest_increasing_limit: bool, + mutate: impl FnOnce(&mut Diagnostic), + ) -> ! + where + T: fmt::Display + + TypeFoldable<TyCtxt<'tcx>> + + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, + <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug, + { + let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit); + mutate(&mut err); + err.emit(); + + self.tcx.sess.abort_if_errors(); + bug!(); + } + + fn build_overflow_error<T>( + &self, + predicate: &T, + span: Span, + suggest_increasing_limit: bool, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> + where + T: fmt::Display + + TypeFoldable<TyCtxt<'tcx>> + + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, + <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug, + { + let predicate = self.resolve_vars_if_possible(predicate.clone()); + let mut pred_str = predicate.to_string(); + + if pred_str.len() > 50 { + // We don't need to save the type to a file, we will be talking about this type already + // in a separate note when we explain the obligation, so it will be available that way. + pred_str = predicate + .print(FmtPrinter::new_with_limit( + self.tcx, + Namespace::TypeNS, + rustc_session::Limit(6), + )) + .unwrap() + .into_buffer(); + } + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0275, + "overflow evaluating the requirement `{}`", + pred_str, + ); + + if suggest_increasing_limit { + self.suggest_new_overflow_limit(&mut err); + } + + err + } + + /// Reports that an overflow has occurred and halts compilation. We + /// halt compilation unconditionally because it is important that + /// overflows never be masked -- they basically represent computations + /// whose result could not be truly determined and thus we can't say + /// if the program type checks or not -- and they are unusual + /// occurrences in any case. + fn report_overflow_obligation<T>( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: ToPredicate<'tcx> + Clone, + { + let predicate = obligation.predicate.clone().to_predicate(self.tcx); + let predicate = self.resolve_vars_if_possible(predicate); + self.report_overflow_error( + &predicate, + obligation.cause.span, + suggest_increasing_limit, + |err| { + self.note_obligation_cause_code( + obligation.cause.body_id, + err, + predicate, + obligation.param_env, + obligation.cause.code(), + &mut vec![], + &mut Default::default(), + ); + }, + ); + } + + fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) { + let suggested_limit = match self.tcx.recursion_limit() { + Limit(0) => Limit(2), + limit => limit * 2, + }; + err.help(format!( + "consider increasing the recursion limit by adding a \ + `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)", + suggested_limit, + self.tcx.crate_name(LOCAL_CRATE), + )); + } + + /// Reports that a cycle was detected which led to overflow and halts + /// compilation. This is equivalent to `report_overflow_obligation` except + /// that we can give a more helpful error message (and, in particular, + /// we do not suggest increasing the overflow limit, which is not + /// going to help). + fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { + let cycle = self.resolve_vars_if_possible(cycle.to_owned()); + assert!(!cycle.is_empty()); + + debug!(?cycle, "report_overflow_error_cycle"); + + // The 'deepest' obligation is most likely to have a useful + // cause 'backtrace' + self.report_overflow_obligation( + cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(), + false, + ); + } + + fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed { + let obligation = self.resolve_vars_if_possible(obligation); + let mut err = self.build_overflow_error(&obligation.predicate, obligation.cause.span, true); + self.note_obligation_cause(&mut err, &obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + err.emit() + } + + fn report_selection_error( + &self, + mut obligation: PredicateObligation<'tcx>, + root_obligation: &PredicateObligation<'tcx>, + error: &SelectionError<'tcx>, + ) { + let tcx = self.tcx; + + if tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError { + dump_proof_tree(root_obligation, self.infcx); + } + + let mut span = obligation.cause.span; + // FIXME: statically guarantee this by tainting after the diagnostic is emitted + self.set_tainted_by_errors( + tcx.sess.delay_span_bug(span, "`report_selection_error` did not emit an error"), + ); + + let mut err = match *error { + SelectionError::Unimplemented => { + // If this obligation was generated as a result of well-formedness checking, see if we + // can get a better error message by performing HIR-based well-formedness checking. + if let ObligationCauseCode::WellFormed(Some(wf_loc)) = + root_obligation.cause.code().peel_derives() + && !obligation.predicate.has_non_region_infer() + { + if let Some(cause) = self + .tcx + .diagnostic_hir_wf_check((tcx.erase_regions(obligation.predicate), *wf_loc)) + { + obligation.cause = cause.clone(); + span = obligation.cause.span; + } + } + + if let ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id, + trait_item_def_id, + kind: _, + } = *obligation.cause.code() + { + self.report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{}`", obligation.predicate), + ) + .emit(); + return; + } + + // Report a const-param specific error + if let ObligationCauseCode::ConstParam(ty) = *obligation.cause.code().peel_derives() + { + self.report_const_param_not_wf(ty, &obligation).emit(); + return; + } + + let bound_predicate = obligation.predicate.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => { + let trait_predicate = bound_predicate.rebind(trait_predicate); + let trait_predicate = self.resolve_vars_if_possible(trait_predicate); + + // FIXME(effects) + let predicate_is_const = false; + + if self.tcx.sess.has_errors().is_some() + && trait_predicate.references_error() + { + return; + } + let trait_ref = trait_predicate.to_poly_trait_ref(); + + let (post_message, pre_message, type_def) = self + .get_parent_trait_ref(obligation.cause.code()) + .map(|(t, s)| { + ( + format!(" in `{t}`"), + format!("within `{t}`, "), + s.map(|s| (format!("within this `{t}`"), s)), + ) + }) + .unwrap_or_default(); + + let OnUnimplementedNote { + message, + label, + note, + parent_label, + append_const_msg, + } = self.on_unimplemented_note(trait_ref, &obligation); + let have_alt_message = message.is_some() || label.is_some(); + let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id()); + let is_unsize = + Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait(); + let (message, note, append_const_msg) = if is_try_conversion { + ( + Some(format!( + "`?` couldn't convert the error to `{}`", + trait_ref.skip_binder().self_ty(), + )), + Some( + "the question mark operation (`?`) implicitly performs a \ + conversion on the error value using the `From` trait" + .to_owned(), + ), + Some(AppendConstMessage::Default), + ) + } else { + (message, note, append_const_msg) + }; + + let err_msg = self.get_standard_error_message( + &trait_predicate, + message, + predicate_is_const, + append_const_msg, + post_message, + ); + + let (err_msg, safe_transmute_explanation) = if Some(trait_ref.def_id()) + == self.tcx.lang_items().transmute_trait() + { + // Recompute the safe transmute reason and use that for the error reporting + match self.get_safe_transmute_error_and_reason( + obligation.clone(), + trait_ref, + span, + ) { + GetSafeTransmuteErrorAndReason::Silent => return, + GetSafeTransmuteErrorAndReason::Error { + err_msg, + safe_transmute_explanation, + } => (err_msg, Some(safe_transmute_explanation)), + } + } else { + (err_msg, None) + }; + + let mut err = struct_span_err!(self.tcx.sess, span, E0277, "{}", err_msg); + + if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { + err.span_label( + ret_span, + format!( + "expected `{}` because of this", + trait_ref.skip_binder().self_ty() + ), + ); + } + + if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() { + self.add_tuple_trait_message( + &obligation.cause.code().peel_derives(), + &mut err, + ); + } + + if Some(trait_ref.def_id()) == tcx.lang_items().drop_trait() + && predicate_is_const + { + err.note("`~const Drop` was renamed to `~const Destruct`"); + err.note("See <https://github.com/rust-lang/rust/pull/94901> for more details"); + } + + let explanation = get_explanation_based_on_obligation( + &obligation, + trait_ref, + &trait_predicate, + pre_message, + ); + + self.check_for_binding_assigned_block_without_tail_expression( + &obligation, + &mut err, + trait_predicate, + ); + if self.suggest_add_reference_to_arg( + &obligation, + &mut err, + trait_predicate, + have_alt_message, + ) { + self.note_obligation_cause(&mut err, &obligation); + err.emit(); + return; + } + if let Some(s) = label { + // If it has a custom `#[rustc_on_unimplemented]` + // error message, let's display it as the label! + err.span_label(span, s); + if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { + // When the self type is a type param We don't need to "the trait + // `std::marker::Sized` is not implemented for `T`" as we will point + // at the type param with a label to suggest constraining it. + err.help(explanation); + } + } else if let Some(custom_explanation) = safe_transmute_explanation { + err.span_label(span, custom_explanation); + } else { + err.span_label(span, explanation); + } + + if let ObligationCauseCode::Coercion { source, target } = + *obligation.cause.code().peel_derives() + { + if Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { + self.suggest_borrowing_for_object_cast( + &mut err, + &root_obligation, + source, + target, + ); + } + } + + let UnsatisfiedConst(unsatisfied_const) = self + .maybe_add_note_for_unsatisfied_const( + &obligation, + trait_ref, + &trait_predicate, + &mut err, + span, + ); + + if let Some((msg, span)) = type_def { + err.span_label(span, msg); + } + if let Some(s) = note { + // If it has a custom `#[rustc_on_unimplemented]` note, let's display it + err.note(s); + } + if let Some(s) = parent_label { + let body = obligation.cause.body_id; + err.span_label(tcx.def_span(body), s); + } + + self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); + self.suggest_dereferencing_index(&obligation, &mut err, trait_predicate); + let mut suggested = + self.suggest_dereferences(&obligation, &mut err, trait_predicate); + suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate); + let impl_candidates = self.find_similar_impl_candidates(trait_predicate); + suggested = if let &[cand] = &impl_candidates[..] { + let cand = cand.trait_ref; + if let (ty::FnPtr(_), ty::FnDef(..)) = + (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) + { + err.span_suggestion( + span.shrink_to_hi(), + format!( + "the trait `{}` is implemented for fn pointer `{}`, try casting using `as`", + cand.print_only_trait_path(), + cand.self_ty(), + ), + format!(" as {}", cand.self_ty()), + Applicability::MaybeIncorrect, + ); + true + } else { + false + } + } else { + false + } || suggested; + suggested |= + self.suggest_remove_reference(&obligation, &mut err, trait_predicate); + suggested |= self.suggest_semicolon_removal( + &obligation, + &mut err, + span, + trait_predicate, + ); + self.note_version_mismatch(&mut err, &trait_ref); + self.suggest_remove_await(&obligation, &mut err); + self.suggest_derive(&obligation, &mut err, trait_predicate); + + if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() { + self.suggest_await_before_try( + &mut err, + &obligation, + trait_predicate, + span, + ); + } + + if self.suggest_add_clone_to_arg(&obligation, &mut err, trait_predicate) { + err.emit(); + return; + } + + if self.suggest_impl_trait(&mut err, &obligation, trait_predicate) { + err.emit(); + return; + } + + if is_unsize { + // If the obligation failed due to a missing implementation of the + // `Unsize` trait, give a pointer to why that might be the case + err.note( + "all implementations of `Unsize` are provided \ + automatically by the compiler, see \ + <https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html> \ + for more information", + ); + } + + let is_fn_trait = tcx.is_fn_trait(trait_ref.def_id()); + let is_target_feature_fn = if let ty::FnDef(def_id, _) = + *trait_ref.skip_binder().self_ty().kind() + { + !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() + } else { + false + }; + if is_fn_trait && is_target_feature_fn { + err.note( + "`#[target_feature]` functions do not implement the `Fn` traits", + ); + } + + self.try_to_add_help_message( + &obligation, + trait_ref, + &trait_predicate, + &mut err, + span, + is_fn_trait, + suggested, + unsatisfied_const, + ); + + // Changing mutability doesn't make a difference to whether we have + // an `Unsize` impl (Fixes ICE in #71036) + if !is_unsize { + self.suggest_change_mut(&obligation, &mut err, trait_predicate); + } + + // If this error is due to `!: Trait` not implemented but `(): Trait` is + // implemented, and fallback has occurred, then it could be due to a + // variable that used to fallback to `()` now falling back to `!`. Issue a + // note informing about the change in behaviour. + if trait_predicate.skip_binder().self_ty().is_never() + && self.fallback_has_occurred + { + let predicate = trait_predicate.map_bound(|trait_pred| { + trait_pred.with_self_ty(self.tcx, Ty::new_unit(self.tcx)) + }); + let unit_obligation = obligation.with(tcx, predicate); + if self.predicate_may_hold(&unit_obligation) { + err.note( + "this error might have been caused by changes to \ + Rust's type-inference algorithm (see issue #48950 \ + <https://github.com/rust-lang/rust/issues/48950> \ + for more information)", + ); + err.help("did you intend to use the type `()` here instead?"); + } + } + + self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause); + self.suggest_desugaring_async_fn_in_trait(&mut err, trait_ref); + + // Return early if the trait is Debug or Display and the invocation + // originates within a standard library macro, because the output + // is otherwise overwhelming and unhelpful (see #85844 for an + // example). + + let in_std_macro = + match obligation.cause.span.ctxt().outer_expn_data().macro_def_id { + Some(macro_def_id) => { + let crate_name = tcx.crate_name(macro_def_id.krate); + crate_name == sym::std || crate_name == sym::core + } + None => false, + }; + + if in_std_macro + && matches!( + self.tcx.get_diagnostic_name(trait_ref.def_id()), + Some(sym::Debug | sym::Display) + ) + { + err.emit(); + return; + } + + err + } + + ty::PredicateKind::Subtype(predicate) => { + // Errors for Subtype predicates show up as + // `FulfillmentErrorCode::CodeSubtypeError`, + // not selection error. + span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) + } + + ty::PredicateKind::Coerce(predicate) => { + // Errors for Coerce predicates show up as + // `FulfillmentErrorCode::CodeSubtypeError`, + // not selection error. + span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate) + } + + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => { + span_bug!( + span, + "outlives clauses should not error outside borrowck. obligation: `{:?}`", + obligation + ) + } + + ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => { + span_bug!( + span, + "projection clauses should be implied from elsewhere. obligation: `{:?}`", + obligation + ) + } + + ty::PredicateKind::ObjectSafe(trait_def_id) => { + let violations = self.tcx.object_safety_violations(trait_def_id); + report_object_safety_error(self.tcx, span, trait_def_id, violations) + } + + ty::PredicateKind::ClosureKind(closure_def_id, closure_args, kind) => { + let found_kind = self.closure_kind(closure_args).unwrap(); + self.report_closure_error(&obligation, closure_def_id, found_kind, kind) + } + + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => { + let ty = self.resolve_vars_if_possible(ty); + match self.tcx.sess.opts.unstable_opts.trait_solver { + TraitSolver::Classic => { + // WF predicates cannot themselves make + // errors. They can only block due to + // ambiguity; otherwise, they always + // degenerate into other obligations + // (which may fail). + span_bug!(span, "WF predicate not satisfied for {:?}", ty); + } + TraitSolver::Next | TraitSolver::NextCoherence => { + // FIXME: we'll need a better message which takes into account + // which bounds actually failed to hold. + self.tcx.sess.struct_span_err( + span, + format!("the type `{ty}` is not well-formed"), + ) + } + } + } + + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) => { + // Errors for `ConstEvaluatable` predicates show up as + // `SelectionError::ConstEvalFailure`, + // not `Unimplemented`. + span_bug!( + span, + "const-evaluatable requirement gave wrong error: `{:?}`", + obligation + ) + } + + ty::PredicateKind::ConstEquate(..) => { + // Errors for `ConstEquate` predicates show up as + // `SelectionError::ConstEvalFailure`, + // not `Unimplemented`. + span_bug!( + span, + "const-equate requirement gave wrong error: `{:?}`", + obligation + ) + } + + ty::PredicateKind::Ambiguous => span_bug!(span, "ambiguous"), + + ty::PredicateKind::AliasRelate(..) => span_bug!( + span, + "AliasRelate predicate should never be the predicate cause of a SelectionError" + ), + + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { + let mut diag = self.tcx.sess.struct_span_err( + span, + format!("the constant `{ct}` is not of type `{ty}`"), + ); + self.note_type_err( + &mut diag, + &obligation.cause, + None, + None, + TypeError::Sorts(ty::error::ExpectedFound::new(true, ty, ct.ty())), + false, + false, + ); + diag + } + } + } + + OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch { + found_trait_ref, + expected_trait_ref, + terr: terr @ TypeError::CyclicTy(_), + }) => self.report_type_parameter_mismatch_cyclic_type_error( + &obligation, + found_trait_ref, + expected_trait_ref, + terr, + ), + OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch { + found_trait_ref, + expected_trait_ref, + terr: _, + }) => { + match self.report_type_parameter_mismatch_error( + &obligation, + span, + found_trait_ref, + expected_trait_ref, + ) { + Some(err) => err, + None => return, + } + } + + SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => self.report_opaque_type_auto_trait_leakage( + &obligation, + def_id, + ), + + TraitNotObjectSafe(did) => { + let violations = self.tcx.object_safety_violations(did); + report_object_safety_error(self.tcx, span, did, violations) + } + + SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsInfer) => { + bug!( + "MentionsInfer should have been handled in `traits/fulfill.rs` or `traits/select/mod.rs`" + ) + } + SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsParam) => { + match self.report_not_const_evaluatable_error(&obligation, span) { + Some(err) => err, + None => return, + } + } + + // Already reported in the query. + SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) | + // Already reported. + Overflow(OverflowError::Error(_)) => return, + + Overflow(_) => { + bug!("overflow should be handled before the `report_selection_error` path"); + } + SelectionError::ErrorReporting => { + bug!("ErrorReporting Overflow should not reach `report_selection_err` call") + } + }; + + self.note_obligation_cause(&mut err, &obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + err.emit(); + } + + fn report_const_param_not_wf( + &self, + ty: Ty<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let span = obligation.cause.span; + + let mut diag = match ty.kind() { + _ if ty.has_param() => { + span_bug!(span, "const param tys cannot mention other generic parameters"); + } + ty::Float(_) => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "`{ty}` is forbidden as the type of a const generic parameter", + ) + } + ty::FnPtr(_) => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "using function pointers as const generic parameters is forbidden", + ) + } + ty::RawPtr(_) => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "using raw pointers as const generic parameters is forbidden", + ) + } + ty::Adt(def, _) => { + // We should probably see if we're *allowed* to derive `ConstParamTy` on the type... + let mut diag = struct_span_err!( + self.tcx.sess, + span, + E0741, + "`{ty}` must implement `ConstParamTy` to be used as the type of a const generic parameter", + ); + // Only suggest derive if this isn't a derived obligation, + // and the struct is local. + if let Some(span) = self.tcx.hir().span_if_local(def.did()) + && obligation.cause.code().parent().is_none() + { + if ty.is_structural_eq_shallow(self.tcx) { + diag.span_suggestion( + span, + "add `#[derive(ConstParamTy)]` to the struct", + "#[derive(ConstParamTy)]\n", + Applicability::MachineApplicable, + ); + } else { + // FIXME(adt_const_params): We should check there's not already an + // overlapping `Eq`/`PartialEq` impl. + diag.span_suggestion( + span, + "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct", + "#[derive(ConstParamTy, PartialEq, Eq)]\n", + Applicability::MachineApplicable, + ); + } + } + diag + } + _ => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "`{ty}` can't be used as a const parameter type", + ) + } + }; + + let mut code = obligation.cause.code(); + let mut pred = obligation.predicate.to_opt_poly_trait_pred(); + while let Some((next_code, next_pred)) = code.parent() { + if let Some(pred) = pred { + let pred = self.instantiate_binder_with_placeholders(pred); + diag.note(format!( + "`{}` must implement `{}`, but it does not", + pred.self_ty(), + pred.print_modifiers_and_trait_path() + )); + } + code = next_code; + pred = next_pred; + } + + diag + } +} + +pub(super) trait InferCtxtPrivExt<'tcx> { + // returns if `cond` not occurring implies that `error` does not occur - i.e., that + // `error` occurring implies that `cond` occurs. + fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool; + + fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>); + + fn report_projection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &MismatchedProjectionTypes<'tcx>, + ); + + fn maybe_detailed_projection_msg( + &self, + pred: ty::ProjectionPredicate<'tcx>, + normalized_ty: ty::Term<'tcx>, + expected_ty: ty::Term<'tcx>, + ) -> Option<String>; + + fn fuzzy_match_tys( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + ignoring_lifetimes: bool, + ) -> Option<CandidateSimilarity>; + + fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str>; + + fn find_similar_impl_candidates( + &self, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> Vec<ImplCandidate<'tcx>>; + + fn report_similar_impl_candidates( + &self, + impl_candidates: &[ImplCandidate<'tcx>], + trait_ref: ty::PolyTraitRef<'tcx>, + body_def_id: LocalDefId, + err: &mut Diagnostic, + other: bool, + param_env: ty::ParamEnv<'tcx>, + ) -> bool; + + fn report_similar_impl_candidates_for_root_obligation( + &self, + obligation: &PredicateObligation<'tcx>, + trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, + body_def_id: LocalDefId, + err: &mut Diagnostic, + ); + + /// Gets the parent trait chain start + fn get_parent_trait_ref( + &self, + code: &ObligationCauseCode<'tcx>, + ) -> Option<(String, Option<Span>)>; + + /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait + /// with the same path as `trait_ref`, a help message about + /// a probable version mismatch is added to `err` + fn note_version_mismatch( + &self, + err: &mut Diagnostic, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) -> bool; + + /// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the + /// `trait_ref`. + /// + /// For this to work, `new_self_ty` must have no escaping bound variables. + fn mk_trait_obligation_with_new_self_ty( + &self, + param_env: ty::ParamEnv<'tcx>, + trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, + ) -> PredicateObligation<'tcx>; + + fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>); + + fn predicate_can_apply( + &self, + param_env: ty::ParamEnv<'tcx>, + pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool; + + fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>); + + fn suggest_unsized_bound_if_applicable( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + ); + + fn annotate_source_of_ambiguity( + &self, + err: &mut Diagnostic, + impls: &[ambiguity::Ambiguity], + predicate: ty::Predicate<'tcx>, + ); + + fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>); + + fn maybe_indirection_for_unsized( + &self, + err: &mut Diagnostic, + item: &'tcx Item<'tcx>, + param: &'tcx GenericParam<'tcx>, + ) -> bool; + + fn is_recursive_obligation( + &self, + obligated_types: &mut Vec<Ty<'tcx>>, + cause_code: &ObligationCauseCode<'tcx>, + ) -> bool; + + fn get_standard_error_message( + &self, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + message: Option<String>, + predicate_is_const: bool, + append_const_msg: Option<AppendConstMessage>, + post_message: String, + ) -> String; + + fn get_safe_transmute_error_and_reason( + &self, + obligation: PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + span: Span, + ) -> GetSafeTransmuteErrorAndReason; + + fn add_tuple_trait_message( + &self, + obligation_cause_code: &ObligationCauseCode<'tcx>, + err: &mut Diagnostic, + ); + + fn try_to_add_help_message( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + err: &mut Diagnostic, + span: Span, + is_fn_trait: bool, + suggested: bool, + unsatisfied_const: bool, + ); + + fn add_help_message_for_fn_trait( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + err: &mut Diagnostic, + implemented_kind: ty::ClosureKind, + params: ty::Binder<'tcx, Ty<'tcx>>, + ); + + fn maybe_add_note_for_unsatisfied_const( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + err: &mut Diagnostic, + span: Span, + ) -> UnsatisfiedConst; + + fn report_closure_error( + &self, + obligation: &PredicateObligation<'tcx>, + closure_def_id: DefId, + found_kind: ty::ClosureKind, + kind: ty::ClosureKind, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + + fn report_type_parameter_mismatch_cyclic_type_error( + &self, + obligation: &PredicateObligation<'tcx>, + found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + terr: TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + + fn report_opaque_type_auto_trait_leakage( + &self, + obligation: &PredicateObligation<'tcx>, + def_id: DefId, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + + fn report_type_parameter_mismatch_error( + &self, + obligation: &PredicateObligation<'tcx>, + span: Span, + found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>; + + fn report_not_const_evaluatable_error( + &self, + obligation: &PredicateObligation<'tcx>, + span: Span, + ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>; +} + +impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { + // returns if `cond` not occurring implies that `error` does not occur - i.e., that + // `error` occurring implies that `cond` occurs. + fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool { + if cond == error { + return true; + } + + // FIXME: It should be possible to deal with `ForAll` in a cleaner way. + let bound_error = error.kind(); + let (cond, error) = match (cond.kind().skip_binder(), bound_error.skip_binder()) { + ( + ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)), + ty::PredicateKind::Clause(ty::ClauseKind::Trait(error)), + ) => (cond, bound_error.rebind(error)), + _ => { + // FIXME: make this work in other cases too. + return false; + } + }; + + for pred in elaborate(self.tcx, std::iter::once(cond)) { + let bound_predicate = pred.kind(); + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(implication)) = + bound_predicate.skip_binder() + { + let error = error.to_poly_trait_ref(); + let implication = bound_predicate.rebind(implication.trait_ref); + // FIXME: I'm just not taking associated types at all here. + // Eventually I'll need to implement param-env-aware + // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic. + let param_env = ty::ParamEnv::empty(); + if self.can_sub(param_env, error, implication) { + debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication); + return true; + } + } + } + + false + } + + #[instrument(skip(self), level = "debug")] + fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) { + if self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError { + dump_proof_tree(&error.root_obligation, self.infcx); + } + + match error.code { + FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { + self.report_selection_error( + error.obligation.clone(), + &error.root_obligation, + selection_error, + ); + } + FulfillmentErrorCode::CodeProjectionError(ref e) => { + self.report_projection_error(&error.obligation, e); + } + FulfillmentErrorCode::CodeAmbiguity { overflow: false } => { + self.maybe_report_ambiguity(&error.obligation); + } + FulfillmentErrorCode::CodeAmbiguity { overflow: true } => { + self.report_overflow_no_abort(error.obligation.clone()); + } + FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { + self.report_mismatched_types( + &error.obligation.cause, + expected_found.expected, + expected_found.found, + *err, + ) + .emit(); + } + FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => { + let mut diag = self.report_mismatched_consts( + &error.obligation.cause, + expected_found.expected, + expected_found.found, + *err, + ); + let code = error.obligation.cause.code().peel_derives().peel_match_impls(); + if let ObligationCauseCode::BindingObligation(..) + | ObligationCauseCode::ItemObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) + | ObligationCauseCode::ExprItemObligation(..) = code + { + self.note_obligation_cause_code( + error.obligation.cause.body_id, + &mut diag, + error.obligation.predicate, + error.obligation.param_env, + code, + &mut vec![], + &mut Default::default(), + ); + } + diag.emit(); + } + FulfillmentErrorCode::CodeCycle(ref cycle) => { + self.report_overflow_obligation_cycle(cycle); + } + } + } + + #[instrument(level = "debug", skip_all)] + fn report_projection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &MismatchedProjectionTypes<'tcx>, + ) { + let predicate = self.resolve_vars_if_possible(obligation.predicate); + + if predicate.references_error() { + return; + } + + self.probe(|_| { + let ocx = ObligationCtxt::new(self); + + // try to find the mismatched types to report the error with. + // + // this can fail if the problem was higher-ranked, in which + // cause I have no idea for a good error message. + let bound_predicate = predicate.kind(); + let (values, err) = if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) = + bound_predicate.skip_binder() + { + let data = self.instantiate_binder_with_fresh_vars( + obligation.cause.span, + infer::LateBoundRegionConversionTime::HigherRankedType, + bound_predicate.rebind(data), + ); + let unnormalized_term = match data.term.unpack() { + ty::TermKind::Ty(_) => Ty::new_projection( + self.tcx, + data.projection_ty.def_id, + data.projection_ty.args, + ) + .into(), + ty::TermKind::Const(ct) => ty::Const::new_unevaluated( + self.tcx, + ty::UnevaluatedConst { + def: data.projection_ty.def_id, + args: data.projection_ty.args, + }, + ct.ty(), + ) + .into(), + }; + // FIXME(-Ztrait-solver=next): For diagnostic purposes, it would be nice + // to deeply normalize this type. + let normalized_term = + ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); + + debug!(?obligation.cause, ?obligation.param_env); + + debug!(?normalized_term, data.ty = ?data.term); + + let is_normalized_term_expected = !matches!( + obligation.cause.code().peel_derives(), + ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::BindingObligation(_, _) + | ObligationCauseCode::ExprItemObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) + | ObligationCauseCode::Coercion { .. } + | ObligationCauseCode::OpaqueType + ); + + // constrain inference variables a bit more to nested obligations from normalize so + // we can have more helpful errors. + // + // we intentionally drop errors from normalization here, + // since the normalization is just done to improve the error message. + let _ = ocx.select_where_possible(); + + if let Err(new_err) = ocx.eq_exp( + &obligation.cause, + obligation.param_env, + is_normalized_term_expected, + normalized_term, + data.term, + ) { + (Some((data, is_normalized_term_expected, normalized_term, data.term)), new_err) + } else { + (None, error.err) + } + } else { + (None, error.err) + }; + + let msg = values + .and_then(|(predicate, _, normalized_term, expected_term)| { + self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term) + }) + .unwrap_or_else(|| { + with_forced_trimmed_paths!(format!( + "type mismatch resolving `{}`", + self.resolve_vars_if_possible(predicate) + .print(FmtPrinter::new_with_limit( + self.tcx, + Namespace::TypeNS, + rustc_session::Limit(10), + )) + .unwrap() + .into_buffer() + )) + }); + let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}"); + + let secondary_span = (|| { + let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) = + predicate.kind().skip_binder() + else { + return None; + }; + + let trait_assoc_item = self.tcx.opt_associated_item(proj.projection_ty.def_id)?; + let trait_assoc_ident = trait_assoc_item.ident(self.tcx); + + let mut associated_items = vec![]; + self.tcx.for_each_relevant_impl( + self.tcx.trait_of_item(proj.projection_ty.def_id)?, + proj.projection_ty.self_ty(), + |impl_def_id| { + associated_items.extend( + self.tcx + .associated_items(impl_def_id) + .in_definition_order() + .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident), + ); + }, + ); + + let [associated_item]: &[ty::AssocItem] = &associated_items[..] else { + return None; + }; + match self.tcx.hir().get_if_local(associated_item.def_id) { + Some( + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Type(_, Some(ty)), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Type(ty), + .. + }), + ) => Some(( + ty.span, + with_forced_trimmed_paths!(Cow::from(format!( + "type mismatch resolving `{}`", + self.resolve_vars_if_possible(predicate) + .print(FmtPrinter::new_with_limit( + self.tcx, + Namespace::TypeNS, + rustc_session::Limit(5), + )) + .unwrap() + .into_buffer() + ))), + )), + _ => None, + } + })(); + + self.note_type_err( + &mut diag, + &obligation.cause, + secondary_span, + values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| { + infer::ValuePairs::Terms(ExpectedFound::new( + is_normalized_ty_expected, + normalized_ty, + expected_ty, + )) + }), + err, + true, + false, + ); + self.note_obligation_cause(&mut diag, obligation); + diag.emit(); + }); + } + + fn maybe_detailed_projection_msg( + &self, + pred: ty::ProjectionPredicate<'tcx>, + normalized_ty: ty::Term<'tcx>, + expected_ty: ty::Term<'tcx>, + ) -> Option<String> { + let trait_def_id = pred.projection_ty.trait_def_id(self.tcx); + let self_ty = pred.projection_ty.self_ty(); + + with_forced_trimmed_paths! { + if Some(pred.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() { + let fn_kind = self_ty.prefix_string(self.tcx); + let item = match self_ty.kind() { + ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(), + _ => self_ty.to_string(), + }; + Some(format!( + "expected `{item}` to be a {fn_kind} that returns `{expected_ty}`, but it \ + returns `{normalized_ty}`", + )) + } else if Some(trait_def_id) == self.tcx.lang_items().future_trait() { + Some(format!( + "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \ + resolves to `{normalized_ty}`" + )) + } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) { + Some(format!( + "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it \ + yields `{normalized_ty}`" + )) + } else { + None + } + } + } + + fn fuzzy_match_tys( + &self, + mut a: Ty<'tcx>, + mut b: Ty<'tcx>, + ignoring_lifetimes: bool, + ) -> Option<CandidateSimilarity> { + /// returns the fuzzy category of a given type, or None + /// if the type can be equated to any type. + fn type_category(tcx: TyCtxt<'_>, t: Ty<'_>) -> Option<u32> { + match t.kind() { + ty::Bool => Some(0), + ty::Char => Some(1), + ty::Str => Some(2), + ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => Some(2), + ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) => Some(4), + ty::Ref(..) | ty::RawPtr(..) => Some(5), + ty::Array(..) | ty::Slice(..) => Some(6), + ty::FnDef(..) | ty::FnPtr(..) => Some(7), + ty::Dynamic(..) => Some(8), + ty::Closure(..) => Some(9), + ty::Tuple(..) => Some(10), + ty::Param(..) => Some(11), + ty::Alias(ty::Projection, ..) => Some(12), + ty::Alias(ty::Inherent, ..) => Some(13), + ty::Alias(ty::Opaque, ..) => Some(14), + ty::Alias(ty::Weak, ..) => Some(15), + ty::Never => Some(16), + ty::Adt(..) => Some(17), + ty::Generator(..) => Some(18), + ty::Foreign(..) => Some(19), + ty::GeneratorWitness(..) => Some(20), + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, + } + } + + let strip_references = |mut t: Ty<'tcx>| -> Ty<'tcx> { + loop { + match t.kind() { + ty::Ref(_, inner, _) | ty::RawPtr(ty::TypeAndMut { ty: inner, .. }) => { + t = *inner + } + _ => break t, + } + } + }; + + if !ignoring_lifetimes { + a = strip_references(a); + b = strip_references(b); + } + + let cat_a = type_category(self.tcx, a)?; + let cat_b = type_category(self.tcx, b)?; + if a == b { + Some(CandidateSimilarity::Exact { ignoring_lifetimes }) + } else if cat_a == cat_b { + match (a.kind(), b.kind()) { + (ty::Adt(def_a, _), ty::Adt(def_b, _)) => def_a == def_b, + (ty::Foreign(def_a), ty::Foreign(def_b)) => def_a == def_b, + // Matching on references results in a lot of unhelpful + // suggestions, so let's just not do that for now. + // + // We still upgrade successful matches to `ignoring_lifetimes: true` + // to prioritize that impl. + (ty::Ref(..) | ty::RawPtr(..), ty::Ref(..) | ty::RawPtr(..)) => { + self.fuzzy_match_tys(a, b, true).is_some() + } + _ => true, + } + .then_some(CandidateSimilarity::Fuzzy { ignoring_lifetimes }) + } else if ignoring_lifetimes { + None + } else { + self.fuzzy_match_tys(a, b, true) + } + } + + fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str> { + self.tcx.hir().body(body_id).generator_kind.map(|gen_kind| match gen_kind { + hir::GeneratorKind::Gen => "a generator", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "an async block", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "an async function", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "an async closure", + }) + } + + fn find_similar_impl_candidates( + &self, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> Vec<ImplCandidate<'tcx>> { + let mut candidates: Vec<_> = self + .tcx + .all_impls(trait_pred.def_id()) + .filter_map(|def_id| { + if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative + || !self.tcx.is_user_visible_dep(def_id.krate) + { + return None; + } + + let imp = self.tcx.impl_trait_ref(def_id).unwrap().skip_binder(); + + self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false).map( + |similarity| ImplCandidate { trait_ref: imp, similarity, impl_def_id: def_id }, + ) + }) + .collect(); + if candidates.iter().any(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })) { + // If any of the candidates is a perfect match, we don't want to show all of them. + // This is particularly relevant for the case of numeric types (as they all have the + // same category). + candidates.retain(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })); + } + candidates + } + + fn report_similar_impl_candidates( + &self, + impl_candidates: &[ImplCandidate<'tcx>], + trait_ref: ty::PolyTraitRef<'tcx>, + body_def_id: LocalDefId, + err: &mut Diagnostic, + other: bool, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + // If we have a single implementation, try to unify it with the trait ref + // that failed. This should uncover a better hint for what *is* implemented. + if let [single] = &impl_candidates { + if self.probe(|_| { + let ocx = ObligationCtxt::new(self); + let obligation_trait_ref = self.instantiate_binder_with_placeholders(trait_ref); + let impl_args = self.fresh_args_for_item(DUMMY_SP, single.impl_def_id); + let impl_trait_ref = ocx.normalize( + &ObligationCause::dummy(), + param_env, + ty::EarlyBinder::bind(single.trait_ref).instantiate(self.tcx, impl_args), + ); + + ocx.register_obligations( + self.tcx + .predicates_of(single.impl_def_id) + .instantiate(self.tcx, impl_args) + .into_iter() + .map(|(clause, _)| { + Obligation::new(self.tcx, ObligationCause::dummy(), param_env, clause) + }), + ); + if !ocx.select_where_possible().is_empty() { + return false; + } + + let mut terrs = vec![]; + for (obligation_arg, impl_arg) in + std::iter::zip(obligation_trait_ref.args, impl_trait_ref.args) + { + if let Err(terr) = + ocx.eq(&ObligationCause::dummy(), param_env, impl_arg, obligation_arg) + { + terrs.push(terr); + } + if !ocx.select_where_possible().is_empty() { + return false; + } + } + + // Literally nothing unified, just give up. + if terrs.len() == impl_trait_ref.args.len() { + return false; + } + + let cand = + self.resolve_vars_if_possible(impl_trait_ref).fold_with(&mut BottomUpFolder { + tcx: self.tcx, + ty_op: |ty| ty, + lt_op: |lt| lt, + ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()), + }); + err.highlighted_help(vec![ + (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle), + ("is".to_string(), Style::Highlight), + (" implemented for `".to_string(), Style::NoStyle), + (cand.self_ty().to_string(), Style::Highlight), + ("`".to_string(), Style::NoStyle), + ]); + + if let [TypeError::Sorts(exp_found)] = &terrs[..] { + let exp_found = self.resolve_vars_if_possible(*exp_found); + err.help(format!( + "for that trait implementation, expected `{}`, found `{}`", + exp_found.expected, exp_found.found + )); + } + + true + }) { + return true; + } + } + + let other = if other { "other " } else { "" }; + let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| { + if candidates.is_empty() { + return false; + } + if let &[cand] = &candidates[..] { + let (desc, mention_castable) = + match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) { + (ty::FnPtr(_), ty::FnDef(..)) => { + (" implemented for fn pointer `", ", cast using `as`") + } + (ty::FnPtr(_), _) => (" implemented for fn pointer `", ""), + _ => (" implemented for `", ""), + }; + err.highlighted_help(vec![ + (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle), + ("is".to_string(), Style::Highlight), + (desc.to_string(), Style::NoStyle), + (cand.self_ty().to_string(), Style::Highlight), + ("`".to_string(), Style::NoStyle), + (mention_castable.to_string(), Style::NoStyle), + ]); + return true; + } + let trait_ref = TraitRef::identity(self.tcx, candidates[0].def_id); + // Check if the trait is the same in all cases. If so, we'll only show the type. + let mut traits: Vec<_> = + candidates.iter().map(|c| c.print_only_trait_path().to_string()).collect(); + traits.sort(); + traits.dedup(); + // FIXME: this could use a better heuristic, like just checking + // that args[1..] is the same. + let all_traits_equal = traits.len() == 1; + + let candidates: Vec<String> = candidates + .into_iter() + .map(|c| { + if all_traits_equal { + format!("\n {}", c.self_ty()) + } else { + format!("\n {c}") + } + }) + .collect(); + + let end = if candidates.len() <= 9 { candidates.len() } else { 8 }; + err.help(format!( + "the following {other}types implement trait `{}`:{}{}", + trait_ref.print_only_trait_path(), + candidates[..end].join(""), + if candidates.len() > 9 { + format!("\nand {} others", candidates.len() - 8) + } else { + String::new() + } + )); + true + }; + + let def_id = trait_ref.def_id(); + if impl_candidates.is_empty() { + if self.tcx.trait_is_auto(def_id) + || self.tcx.lang_items().iter().any(|(_, id)| id == def_id) + || self.tcx.get_diagnostic_name(def_id).is_some() + { + // Mentioning implementers of `Copy`, `Debug` and friends is not useful. + return false; + } + let mut impl_candidates: Vec<_> = self + .tcx + .all_impls(def_id) + // Ignore automatically derived impls and `!Trait` impls. + .filter(|&def_id| { + self.tcx.impl_polarity(def_id) != ty::ImplPolarity::Negative + || self.tcx.is_automatically_derived(def_id) + }) + .filter_map(|def_id| self.tcx.impl_trait_ref(def_id)) + .map(ty::EarlyBinder::instantiate_identity) + .filter(|trait_ref| { + let self_ty = trait_ref.self_ty(); + // Avoid mentioning type parameters. + if let ty::Param(_) = self_ty.kind() { + false + } + // Avoid mentioning types that are private to another crate + else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { + // FIXME(compiler-errors): This could be generalized, both to + // be more granular, and probably look past other `#[fundamental]` + // types, too. + self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) + } else { + true + } + }) + .collect(); + + impl_candidates.sort(); + impl_candidates.dedup(); + return report(impl_candidates, err); + } + + // Sort impl candidates so that ordering is consistent for UI tests. + // because the ordering of `impl_candidates` may not be deterministic: + // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507 + // + // Prefer more similar candidates first, then sort lexicographically + // by their normalized string representation. + let mut impl_candidates: Vec<_> = impl_candidates + .iter() + .cloned() + .map(|mut cand| { + // Fold the consts so that they shows up as, e.g., `10` + // instead of `core::::array::{impl#30}::{constant#0}`. + cand.trait_ref = cand.trait_ref.fold_with(&mut BottomUpFolder { + tcx: self.tcx, + ty_op: |ty| ty, + lt_op: |lt| lt, + ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()), + }); + cand + }) + .collect(); + impl_candidates.sort_by_key(|cand| (cand.similarity, cand.trait_ref)); + let mut impl_candidates: Vec<_> = + impl_candidates.into_iter().map(|cand| cand.trait_ref).collect(); + impl_candidates.dedup(); + + report(impl_candidates, err) + } + + fn report_similar_impl_candidates_for_root_obligation( + &self, + obligation: &PredicateObligation<'tcx>, + trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, + body_def_id: LocalDefId, + err: &mut Diagnostic, + ) { + // This is *almost* equivalent to + // `obligation.cause.code().peel_derives()`, but it gives us the + // trait predicate for that corresponding root obligation. This + // lets us get a derived obligation from a type parameter, like + // when calling `string.strip_suffix(p)` where `p` is *not* an + // implementer of `Pattern<'_>`. + let mut code = obligation.cause.code(); + let mut trait_pred = trait_predicate; + let mut peeled = false; + while let Some((parent_code, parent_trait_pred)) = code.parent() { + code = parent_code; + if let Some(parent_trait_pred) = parent_trait_pred { + trait_pred = parent_trait_pred; + peeled = true; + } + } + let def_id = trait_pred.def_id(); + // Mention *all* the `impl`s for the *top most* obligation, the + // user might have meant to use one of them, if any found. We skip + // auto-traits or fundamental traits that might not be exactly what + // the user might expect to be presented with. Instead this is + // useful for less general traits. + if peeled + && !self.tcx.trait_is_auto(def_id) + && !self.tcx.lang_items().iter().any(|(_, id)| id == def_id) + { + let trait_ref = trait_pred.to_poly_trait_ref(); + let impl_candidates = self.find_similar_impl_candidates(trait_pred); + self.report_similar_impl_candidates( + &impl_candidates, + trait_ref, + body_def_id, + err, + true, + obligation.param_env, + ); + } + } + + /// Gets the parent trait chain start + fn get_parent_trait_ref( + &self, + code: &ObligationCauseCode<'tcx>, + ) -> Option<(String, Option<Span>)> { + match code { + ObligationCauseCode::BuiltinDerivedObligation(data) => { + let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); + match self.get_parent_trait_ref(&data.parent_code) { + Some(t) => Some(t), + None => { + let ty = parent_trait_ref.skip_binder().self_ty(); + let span = TyCategory::from_ty(self.tcx, ty) + .map(|(_, def_id)| self.tcx.def_span(def_id)); + Some((ty.to_string(), span)) + } + } + } + ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => { + self.get_parent_trait_ref(&parent_code) + } + _ => None, + } + } + + /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait + /// with the same path as `trait_ref`, a help message about + /// a probable version mismatch is added to `err` + fn note_version_mismatch( + &self, + err: &mut Diagnostic, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) -> bool { + let get_trait_impls = |trait_def_id| { + let mut trait_impls = vec![]; + self.tcx.for_each_relevant_impl( + trait_def_id, + trait_ref.skip_binder().self_ty(), + |impl_def_id| { + trait_impls.push(impl_def_id); + }, + ); + trait_impls + }; + + let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); + let traits_with_same_path: std::collections::BTreeSet<_> = self + .tcx + .all_traits() + .filter(|trait_def_id| *trait_def_id != trait_ref.def_id()) + .filter(|trait_def_id| self.tcx.def_path_str(*trait_def_id) == required_trait_path) + .collect(); + let mut suggested = false; + for trait_with_same_path in traits_with_same_path { + let trait_impls = get_trait_impls(trait_with_same_path); + if trait_impls.is_empty() { + continue; + } + let impl_spans: Vec<_> = + trait_impls.iter().map(|impl_def_id| self.tcx.def_span(*impl_def_id)).collect(); + err.span_help( + impl_spans, + format!("trait impl{} with same name found", pluralize!(trait_impls.len())), + ); + let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); + let crate_msg = + format!("perhaps two different versions of crate `{trait_crate}` are being used?"); + err.note(crate_msg); + suggested = true; + } + suggested + } + + fn mk_trait_obligation_with_new_self_ty( + &self, + param_env: ty::ParamEnv<'tcx>, + trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, + ) -> PredicateObligation<'tcx> { + let trait_pred = + trait_ref_and_ty.map_bound(|(tr, new_self_ty)| tr.with_self_ty(self.tcx, new_self_ty)); + + Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred) + } + + #[instrument(skip(self), level = "debug")] + fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) { + // Unable to successfully determine, probably means + // insufficient type information, but could mean + // ambiguous impls. The latter *ought* to be a + // coherence violation, so we don't report it here. + + let predicate = self.resolve_vars_if_possible(obligation.predicate); + let span = obligation.cause.span; + + debug!(?predicate, obligation.cause.code = ?obligation.cause.code()); + + // Ambiguity errors are often caused as fallout from earlier errors. + // We ignore them if this `infcx` is tainted in some cases below. + + let bound_predicate = predicate.kind(); + let mut err = match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { + let trait_ref = bound_predicate.rebind(data.trait_ref); + debug!(?trait_ref); + + if predicate.references_error() { + return; + } + + // This is kind of a hack: it frequently happens that some earlier + // error prevents types from being fully inferred, and then we get + // a bunch of uninteresting errors saying something like "<generic + // #0> doesn't implement Sized". It may even be true that we + // could just skip over all checks where the self-ty is an + // inference variable, but I was afraid that there might be an + // inference variable created, registered as an obligation, and + // then never forced by writeback, and hence by skipping here we'd + // be ignoring the fact that we don't KNOW the type works + // out. Though even that would probably be harmless, given that + // we're only talking about builtin traits, which are known to be + // inhabited. We used to check for `self.tcx.sess.has_errors()` to + // avoid inundating the user with unnecessary errors, but we now + // check upstream for type errors and don't add the obligations to + // begin with in those cases. + if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { + if let None = self.tainted_by_errors() { + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + trait_ref.self_ty().skip_binder().into(), + ErrorCode::E0282, + false, + ) + .emit(); + } + return; + } + + // Typically, this ambiguity should only happen if + // there are unresolved type inference variables + // (otherwise it would suggest a coherence + // failure). But given #21974 that is not necessarily + // the case -- we can have multiple where clauses that + // are only distinguished by a region, which results + // in an ambiguity even when all types are fully + // known, since we don't dispatch based on region + // relationships. + + // Pick the first substitution that still contains inference variables as the one + // we're going to emit an error for. If there are none (see above), fall back to + // a more general error. + let subst = data.trait_ref.args.iter().find(|s| s.has_non_region_infer()); + + let mut err = if let Some(subst) = subst { + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + subst, + ErrorCode::E0283, + true, + ) + } else { + struct_span_err!( + self.tcx.sess, + span, + E0283, + "type annotations needed: cannot satisfy `{}`", + predicate, + ) + }; + + let mut ambiguities = ambiguity::recompute_applicable_impls( + self.infcx, + &obligation.with(self.tcx, trait_ref), + ); + let has_non_region_infer = + trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer()); + // It doesn't make sense to talk about applicable impls if there are more than a + // handful of them. If there are a lot of them, but only a few of them have no type + // params, we only show those, as they are more likely to be useful/intended. + if ambiguities.len() > 5 { + let infcx = self.infcx; + if !ambiguities.iter().all(|option| match option { + DefId(did) => infcx.fresh_args_for_item(DUMMY_SP, *did).is_empty(), + ParamEnv(_) => true, + }) { + // If not all are blanket impls, we filter blanked impls out. + ambiguities.retain(|option| match option { + DefId(did) => infcx.fresh_args_for_item(DUMMY_SP, *did).is_empty(), + ParamEnv(_) => true, + }); + } + } + if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { + if self.tainted_by_errors().is_some() && subst.is_none() { + // If `subst.is_none()`, then this is probably two param-env + // candidates or impl candidates that are equal modulo lifetimes. + // Therefore, if we've already emitted an error, just skip this + // one, since it's not particularly actionable. + err.cancel(); + return; + } + self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate); + } else { + if self.tainted_by_errors().is_some() { + err.cancel(); + return; + } + err.note(format!("cannot satisfy `{predicate}`")); + let impl_candidates = self + .find_similar_impl_candidates(predicate.to_opt_poly_trait_pred().unwrap()); + if impl_candidates.len() < 40 { + self.report_similar_impl_candidates( + impl_candidates.as_slice(), + trait_ref, + obligation.cause.body_id, + &mut err, + false, + obligation.param_env, + ); + } + } + + if let ObligationCauseCode::ItemObligation(def_id) + | ObligationCauseCode::ExprItemObligation(def_id, ..) = *obligation.cause.code() + { + self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); + } + + if let Some(ty::GenericArgKind::Type(_)) = subst.map(|subst| subst.unpack()) + && let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) + { + let mut expr_finder = FindExprBySpan::new(span); + expr_finder.visit_expr(&self.tcx.hir().body(body_id).value); + + if let Some(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), .. } + ) = expr_finder.result + && let [ + .., + trait_path_segment @ hir::PathSegment { + res: Res::Def(DefKind::Trait, trait_id), + .. + }, + hir::PathSegment { + ident: assoc_item_name, + res: Res::Def(_, item_id), + .. + } + ] = path.segments + && data.trait_ref.def_id == *trait_id + && self.tcx.trait_of_item(*item_id) == Some(*trait_id) + && let None = self.tainted_by_errors() + { + let (verb, noun) = match self.tcx.associated_item(item_id).kind { + ty::AssocKind::Const => ("refer to the", "constant"), + ty::AssocKind::Fn => ("call", "function"), + // This is already covered by E0223, but this following single match + // arm doesn't hurt here. + ty::AssocKind::Type => ("refer to the", "type"), + }; + + // Replace the more general E0283 with a more specific error + err.cancel(); + err = self.tcx.sess.struct_span_err_with_code( + span, + format!( + "cannot {verb} associated {noun} on trait without specifying the \ + corresponding `impl` type", + ), + rustc_errors::error_code!(E0790), + ); + + if let Some(local_def_id) = data.trait_ref.def_id.as_local() + && let Some(hir::Node::Item(hir::Item { + ident: trait_name, + kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), + .. + })) = self.tcx.hir().find_by_def_id(local_def_id) + && let Some(method_ref) = trait_item_refs + .iter() + .find(|item_ref| item_ref.ident == *assoc_item_name) + { + err.span_label( + method_ref.span, + format!("`{trait_name}::{assoc_item_name}` defined here"), + ); + } + + err.span_label(span, format!("cannot {verb} associated {noun} of trait")); + + let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id); + + if let Some(impl_def_id) = + trait_impls.non_blanket_impls().values().flatten().next() + { + let non_blanket_impl_count = + trait_impls.non_blanket_impls().values().flatten().count(); + // If there is only one implementation of the trait, suggest using it. + // Otherwise, use a placeholder comment for the implementation. + let (message, self_type) = if non_blanket_impl_count == 1 { + ( + "use the fully-qualified path to the only available \ + implementation", + format!( + "{}", + self.tcx.type_of(impl_def_id).instantiate_identity() + ), + ) + } else { + ( + "use a fully-qualified path to a specific available \ + implementation", + "/* self type */".to_string(), + ) + }; + let mut suggestions = vec![( + path.span.shrink_to_lo(), + format!("<{self_type} as "), + )]; + if let Some(generic_arg) = trait_path_segment.args { + let between_span = trait_path_segment.ident.span.between(generic_arg.span_ext); + // get rid of :: between Trait and <type> + // must be '::' between them, otherwise the parser won't accept the code + suggestions.push((between_span, "".to_string(),)); + suggestions.push((generic_arg.span_ext.shrink_to_hi(), ">".to_string())); + } else { + suggestions.push((trait_path_segment.ident.span.shrink_to_hi(), ">".to_string())); + } + err.multipart_suggestion( + message, + suggestions, + Applicability::MaybeIncorrect + ); + } + } + }; + + err + } + + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + // Same hacky approach as above to avoid deluging user + // with error messages. + if arg.references_error() + || self.tcx.sess.has_errors().is_some() + || self.tainted_by_errors().is_some() + { + return; + } + + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + arg, + ErrorCode::E0282, + false, + ) + } + + ty::PredicateKind::Subtype(data) => { + if data.references_error() + || self.tcx.sess.has_errors().is_some() + || self.tainted_by_errors().is_some() + { + // no need to overload user in such cases + return; + } + let SubtypePredicate { a_is_expected: _, a, b } = data; + // both must be type variables, or the other would've been instantiated + assert!(a.is_ty_var() && b.is_ty_var()); + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + a.into(), + ErrorCode::E0282, + true, + ) + } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { + if predicate.references_error() || self.tainted_by_errors().is_some() { + return; + } + let subst = data + .projection_ty + .args + .iter() + .chain(Some(data.term.into_arg())) + .find(|g| g.has_non_region_infer()); + if let Some(subst) = subst { + let mut err = self.emit_inference_failure_err( + obligation.cause.body_id, + span, + subst, + ErrorCode::E0284, + true, + ); + err.note(format!("cannot satisfy `{predicate}`")); + err + } else { + // If we can't find a substitution, just print a generic error + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ); + err.span_label(span, format!("cannot satisfy `{predicate}`")); + err + } + } + + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => { + if predicate.references_error() || self.tainted_by_errors().is_some() { + return; + } + let subst = data.walk().find(|g| g.is_non_region_infer()); + if let Some(subst) = subst { + let err = self.emit_inference_failure_err( + obligation.cause.body_id, + span, + subst, + ErrorCode::E0284, + true, + ); + err + } else { + // If we can't find a substitution, just print a generic error + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ); + err.span_label(span, format!("cannot satisfy `{predicate}`")); + err + } + } + _ => { + if self.tcx.sess.has_errors().is_some() || self.tainted_by_errors().is_some() { + return; + } + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ); + err.span_label(span, format!("cannot satisfy `{predicate}`")); + err + } + }; + self.note_obligation_cause(&mut err, obligation); + err.emit(); + } + + fn annotate_source_of_ambiguity( + &self, + err: &mut Diagnostic, + ambiguities: &[ambiguity::Ambiguity], + predicate: ty::Predicate<'tcx>, + ) { + let mut spans = vec![]; + let mut crates = vec![]; + let mut post = vec![]; + let mut has_param_env = false; + for ambiguity in ambiguities { + match ambiguity { + ambiguity::Ambiguity::DefId(impl_def_id) => { + match self.tcx.span_of_impl(*impl_def_id) { + Ok(span) => spans.push(span), + Err(name) => { + crates.push(name); + if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) { + post.push(header); + } + } + } + } + ambiguity::Ambiguity::ParamEnv(span) => { + has_param_env = true; + spans.push(*span); + } + } + } + let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect(); + crate_names.sort(); + crate_names.dedup(); + post.sort(); + post.dedup(); + + if self.tainted_by_errors().is_some() + && (crate_names.len() == 1 + && spans.len() == 0 + && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str()) + || predicate.visit_with(&mut HasNumericInferVisitor).is_break()) + { + // Avoid complaining about other inference issues for expressions like + // `42 >> 1`, where the types are still `{integer}`, but we want to + // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too? + // NOTE(eddyb) this was `.cancel()`, but `err` + // is borrowed, so we can't fully defuse it. + err.downgrade_to_delayed_bug(); + return; + } + + let msg = format!( + "multiple `impl`s{} satisfying `{}` found", + if has_param_env { " or `where` clauses" } else { "" }, + predicate + ); + let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { + format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),) + } else if post.len() == 1 { + format!(": `{}`", post[0]) + } else { + String::new() + }; + + match (spans.len(), crates.len(), crate_names.len()) { + (0, 0, 0) => { + err.note(format!("cannot satisfy `{predicate}`")); + } + (0, _, 1) => { + err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,)); + } + (0, _, _) => { + err.note(format!( + "{} in the following crates: {}{}", + msg, + crate_names.join(", "), + post, + )); + } + (_, 0, 0) => { + let span: MultiSpan = spans.into(); + err.span_note(span, msg); + } + (_, 1, 1) => { + let span: MultiSpan = spans.into(); + err.span_note(span, msg); + err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,)); + } + _ => { + let span: MultiSpan = spans.into(); + err.span_note(span, msg); + err.note(format!( + "and more `impl`s found in the following crates: {}{}", + crate_names.join(", "), + post, + )); + } + } + } + + /// Returns `true` if the trait predicate may apply for *some* assignment + /// to the type parameters. + fn predicate_can_apply( + &self, + param_env: ty::ParamEnv<'tcx>, + pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + struct ParamToVarFolder<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, + var_map: FxHashMap<Ty<'tcx>, Ty<'tcx>>, + } + + impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ParamToVarFolder<'a, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Param(_) = *ty.kind() { + let infcx = self.infcx; + *self.var_map.entry(ty).or_insert_with(|| { + infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: DUMMY_SP, + }) + }) + } else { + ty.super_fold_with(self) + } + } + } + + self.probe(|_| { + let cleaned_pred = + pred.fold_with(&mut ParamToVarFolder { infcx: self, var_map: Default::default() }); + + let InferOk { value: cleaned_pred, .. } = + self.infcx.at(&ObligationCause::dummy(), param_env).normalize(cleaned_pred); + + let obligation = + Obligation::new(self.tcx, ObligationCause::dummy(), param_env, cleaned_pred); + + self.predicate_may_hold(&obligation) + }) + } + + fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>) { + // First, attempt to add note to this error with an async-await-specific + // message, and fall back to regular note otherwise. + if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { + self.note_obligation_cause_code( + obligation.cause.body_id, + err, + obligation.predicate, + obligation.param_env, + obligation.cause.code(), + &mut vec![], + &mut Default::default(), + ); + self.suggest_unsized_bound_if_applicable(err, obligation); + } + } + + #[instrument(level = "debug", skip_all)] + fn suggest_unsized_bound_if_applicable( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + ) { + let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = + obligation.predicate.kind().skip_binder() + else { + return; + }; + let (ObligationCauseCode::BindingObligation(item_def_id, span) + | ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..)) = + *obligation.cause.code().peel_derives() + else { + return; + }; + debug!(?pred, ?item_def_id, ?span); + + let (Some(node), true) = ( + self.tcx.hir().get_if_local(item_def_id), + Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), + ) else { + return; + }; + self.maybe_suggest_unsized_generics(err, span, node); + } + + #[instrument(level = "debug", skip_all)] + fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>) { + let Some(generics) = node.generics() else { + return; + }; + let sized_trait = self.tcx.lang_items().sized_trait(); + debug!(?generics.params); + debug!(?generics.predicates); + let Some(param) = generics.params.iter().find(|param| param.span == span) else { + return; + }; + // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit + // `Sized` bound is there intentionally and we don't need to suggest relaxing it. + let explicitly_sized = generics + .bounds_for_param(param.def_id) + .flat_map(|bp| bp.bounds) + .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait); + if explicitly_sized { + return; + } + debug!(?param); + match node { + hir::Node::Item( + item @ hir::Item { + // Only suggest indirection for uses of type parameters in ADTs. + kind: + hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..), + .. + }, + ) => { + if self.maybe_indirection_for_unsized(err, item, param) { + return; + } + } + _ => {} + }; + // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`. + let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param.def_id) + { + (s, " +") + } else { + (span.shrink_to_hi(), ":") + }; + err.span_suggestion_verbose( + span, + "consider relaxing the implicit `Sized` restriction", + format!("{separator} ?Sized"), + Applicability::MachineApplicable, + ); + } + + fn maybe_indirection_for_unsized( + &self, + err: &mut Diagnostic, + item: &Item<'tcx>, + param: &GenericParam<'tcx>, + ) -> bool { + // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a + // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);` + // is not. Look for invalid "bare" parameter uses, and suggest using indirection. + let mut visitor = + FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false }; + visitor.visit_item(item); + if visitor.invalid_spans.is_empty() { + return false; + } + let mut multispan: MultiSpan = param.span.into(); + multispan.push_span_label( + param.span, + format!("this could be changed to `{}: ?Sized`...", param.name.ident()), + ); + for sp in visitor.invalid_spans { + multispan.push_span_label( + sp, + format!("...if indirection were used here: `Box<{}>`", param.name.ident()), + ); + } + err.span_help( + multispan, + format!( + "you could relax the implicit `Sized` bound on `{T}` if it were \ + used through indirection like `&{T}` or `Box<{T}>`", + T = param.name.ident(), + ), + ); + true + } + + fn is_recursive_obligation( + &self, + obligated_types: &mut Vec<Ty<'tcx>>, + cause_code: &ObligationCauseCode<'tcx>, + ) -> bool { + if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code { + let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); + let self_ty = parent_trait_ref.skip_binder().self_ty(); + if obligated_types.iter().any(|ot| ot == &self_ty) { + return true; + } + if let ty::Adt(def, args) = self_ty.kind() + && let [arg] = &args[..] + && let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Adt(inner_def, _) = ty.kind() + && inner_def == def + { + return true; + } + } + false + } + + fn get_standard_error_message( + &self, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + message: Option<String>, + predicate_is_const: bool, + append_const_msg: Option<AppendConstMessage>, + post_message: String, + ) -> String { + message + .and_then(|cannot_do_this| { + match (predicate_is_const, append_const_msg) { + // do nothing if predicate is not const + (false, _) => Some(cannot_do_this), + // suggested using default post message + (true, Some(AppendConstMessage::Default)) => { + Some(format!("{cannot_do_this} in const contexts")) + } + // overridden post message + (true, Some(AppendConstMessage::Custom(custom_msg))) => { + Some(format!("{cannot_do_this}{custom_msg}")) + } + // fallback to generic message + (true, None) => None, + } + }) + .unwrap_or_else(|| { + format!("the trait bound `{trait_predicate}` is not satisfied{post_message}") + }) + } + + fn get_safe_transmute_error_and_reason( + &self, + obligation: PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + span: Span, + ) -> GetSafeTransmuteErrorAndReason { + use rustc_transmute::Answer; + + // Erase regions because layout code doesn't particularly care about regions. + let trait_ref = self.tcx.erase_regions(self.tcx.erase_late_bound_regions(trait_ref)); + + let src_and_dst = rustc_transmute::Types { + dst: trait_ref.args.type_at(0), + src: trait_ref.args.type_at(1), + }; + let scope = trait_ref.args.type_at(2); + let Some(assume) = rustc_transmute::Assume::from_const( + self.infcx.tcx, + obligation.param_env, + trait_ref.args.const_at(3), + ) else { + span_bug!( + span, + "Unable to construct rustc_transmute::Assume where it was previously possible" + ); + }; + + match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( + obligation.cause, + src_and_dst, + scope, + assume, + ) { + Answer::No(reason) => { + let dst = trait_ref.args.type_at(0); + let src = trait_ref.args.type_at(1); + let err_msg = format!( + "`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`" + ); + let safe_transmute_explanation = match reason { + rustc_transmute::Reason::SrcIsUnspecified => { + format!("`{src}` does not have a well-specified layout") + } + + rustc_transmute::Reason::DstIsUnspecified => { + format!("`{dst}` does not have a well-specified layout") + } + + rustc_transmute::Reason::DstIsBitIncompatible => { + format!("At least one value of `{src}` isn't a bit-valid value of `{dst}`") + } + + rustc_transmute::Reason::DstIsPrivate => format!( + "`{dst}` is or contains a type or field that is not visible in that scope" + ), + rustc_transmute::Reason::DstIsTooBig => { + format!("The size of `{src}` is smaller than the size of `{dst}`") + } + rustc_transmute::Reason::SrcSizeOverflow => { + format!( + "values of the type `{src}` are too big for the current architecture" + ) + } + rustc_transmute::Reason::DstSizeOverflow => { + format!( + "values of the type `{dst}` are too big for the current architecture" + ) + } + rustc_transmute::Reason::DstHasStricterAlignment { + src_min_align, + dst_min_align, + } => { + format!( + "The minimum alignment of `{src}` ({src_min_align}) should be greater than that of `{dst}` ({dst_min_align})" + ) + } + rustc_transmute::Reason::DstIsMoreUnique => { + format!("`{src}` is a shared reference, but `{dst}` is a unique reference") + } + // Already reported by rustc + rustc_transmute::Reason::TypeError => { + return GetSafeTransmuteErrorAndReason::Silent; + } + rustc_transmute::Reason::SrcLayoutUnknown => { + format!("`{src}` has an unknown layout") + } + rustc_transmute::Reason::DstLayoutUnknown => { + format!("`{dst}` has an unknown layout") + } + }; + GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation } + } + // Should never get a Yes at this point! We already ran it before, and did not get a Yes. + Answer::Yes => span_bug!( + span, + "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes", + ), + other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"), + } + } + + fn add_tuple_trait_message( + &self, + obligation_cause_code: &ObligationCauseCode<'tcx>, + err: &mut Diagnostic, + ) { + match obligation_cause_code { + ObligationCauseCode::RustCall => { + err.set_primary_message("functions with the \"rust-call\" ABI must take a single non-self tuple argument"); + } + ObligationCauseCode::BindingObligation(def_id, _) + | ObligationCauseCode::ItemObligation(def_id) + if self.tcx.is_fn_trait(*def_id) => + { + err.code(rustc_errors::error_code!(E0059)); + err.set_primary_message(format!( + "type parameter to bare `{}` trait must be a tuple", + self.tcx.def_path_str(*def_id) + )); + } + _ => {} + } + } + + fn try_to_add_help_message( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + err: &mut Diagnostic, + span: Span, + is_fn_trait: bool, + suggested: bool, + unsatisfied_const: bool, + ) { + let body_def_id = obligation.cause.body_id; + let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } = + obligation.cause.code() + { + *rhs_span + } else { + span + }; + + // Try to report a help message + if is_fn_trait + && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( + obligation.param_env, + trait_ref.self_ty(), + trait_predicate.skip_binder().polarity, + ) + { + self.add_help_message_for_fn_trait(trait_ref, err, implemented_kind, params); + } else if !trait_ref.has_non_region_infer() + && self.predicate_can_apply(obligation.param_env, *trait_predicate) + { + // If a where-clause may be useful, remind the + // user that they can add it. + // + // don't display an on-unimplemented note, as + // these notes will often be of the form + // "the type `T` can't be frobnicated" + // which is somewhat confusing. + self.suggest_restricting_param_bound( + err, + *trait_predicate, + None, + obligation.cause.body_id, + ); + } else if trait_ref.def_id().is_local() + && self.tcx.trait_impls_of(trait_ref.def_id()).is_empty() + && !self.tcx.trait_is_auto(trait_ref.def_id()) + && !self.tcx.trait_is_alias(trait_ref.def_id()) + { + err.span_help( + self.tcx.def_span(trait_ref.def_id()), + crate::fluent_generated::trait_selection_trait_has_no_impls, + ); + } else if !suggested && !unsatisfied_const { + // Can't show anything else useful, try to find similar impls. + let impl_candidates = self.find_similar_impl_candidates(*trait_predicate); + if !self.report_similar_impl_candidates( + &impl_candidates, + trait_ref, + body_def_id, + err, + true, + obligation.param_env, + ) { + self.report_similar_impl_candidates_for_root_obligation( + &obligation, + *trait_predicate, + body_def_id, + err, + ); + } + + self.suggest_convert_to_slice( + err, + obligation, + trait_ref, + impl_candidates.as_slice(), + span, + ); + } + } + + fn add_help_message_for_fn_trait( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + err: &mut Diagnostic, + implemented_kind: ty::ClosureKind, + params: ty::Binder<'tcx, Ty<'tcx>>, + ) { + // If the type implements `Fn`, `FnMut`, or `FnOnce`, suppress the following + // suggestion to add trait bounds for the type, since we only typically implement + // these traits once. + + // Note if the `FnMut` or `FnOnce` is less general than the trait we're trying + // to implement. + let selected_kind = self + .tcx + .fn_trait_kind_from_def_id(trait_ref.def_id()) + .expect("expected to map DefId to ClosureKind"); + if !implemented_kind.extends(selected_kind) { + err.note(format!( + "`{}` implements `{}`, but it must implement `{}`, which is more general", + trait_ref.skip_binder().self_ty(), + implemented_kind, + selected_kind + )); + } + + // Note any argument mismatches + let given_ty = params.skip_binder(); + let expected_ty = trait_ref.skip_binder().args.type_at(1); + if let ty::Tuple(given) = given_ty.kind() + && let ty::Tuple(expected) = expected_ty.kind() + { + if expected.len() != given.len() { + // Note number of types that were expected and given + err.note( + format!( + "expected a closure taking {} argument{}, but one taking {} argument{} was given", + given.len(), + pluralize!(given.len()), + expected.len(), + pluralize!(expected.len()), + ) + ); + } else if !self.same_type_modulo_infer(given_ty, expected_ty) { + // Print type mismatch + let (expected_args, given_args) = + self.cmp(given_ty, expected_ty); + err.note_expected_found( + &"a closure with arguments", + expected_args, + &"a closure with arguments", + given_args, + ); + } + } + } + + fn maybe_add_note_for_unsatisfied_const( + &self, + _obligation: &PredicateObligation<'tcx>, + _trait_ref: ty::PolyTraitRef<'tcx>, + _trait_predicate: &ty::PolyTraitPredicate<'tcx>, + _err: &mut Diagnostic, + _span: Span, + ) -> UnsatisfiedConst { + let unsatisfied_const = UnsatisfiedConst(false); + // FIXME(effects) + unsatisfied_const + } + + fn report_closure_error( + &self, + obligation: &PredicateObligation<'tcx>, + closure_def_id: DefId, + found_kind: ty::ClosureKind, + kind: ty::ClosureKind, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let closure_span = self.tcx.def_span(closure_def_id); + + let mut err = ClosureKindMismatch { + closure_span, + expected: kind, + found: found_kind, + cause_span: obligation.cause.span, + fn_once_label: None, + fn_mut_label: None, + }; + + // Additional context information explaining why the closure only implements + // a particular trait. + if let Some(typeck_results) = &self.typeck_results { + let hir_id = self.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); + match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) { + (ty::ClosureKind::FnOnce, Some((span, place))) => { + err.fn_once_label = Some(ClosureFnOnceLabel { + span: *span, + place: ty::place_to_string_for_capture(self.tcx, &place), + }) + } + (ty::ClosureKind::FnMut, Some((span, place))) => { + err.fn_mut_label = Some(ClosureFnMutLabel { + span: *span, + place: ty::place_to_string_for_capture(self.tcx, &place), + }) + } + _ => {} + } + } + + self.tcx.sess.create_err(err) + } + + fn report_type_parameter_mismatch_cyclic_type_error( + &self, + obligation: &PredicateObligation<'tcx>, + found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + terr: TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let self_ty = found_trait_ref.self_ty().skip_binder(); + let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() { + ( + ObligationCause::dummy_with_span(self.tcx.def_span(def_id)), + TypeError::CyclicTy(self_ty), + ) + } else { + (obligation.cause.clone(), terr) + }; + self.report_and_explain_type_error( + TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref), + terr, + ) + } + + fn report_opaque_type_auto_trait_leakage( + &self, + obligation: &PredicateObligation<'tcx>, + def_id: DefId, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let name = match self.tcx.opaque_type_origin(def_id.expect_local()) { + hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { + "opaque type".to_string() + } + hir::OpaqueTyOrigin::TyAlias { .. } => { + format!("`{}`", self.tcx.def_path_debug_str(def_id)) + } + }; + let mut err = self.tcx.sess.struct_span_err( + obligation.cause.span, + format!("cannot check whether the hidden type of {name} satisfies auto traits"), + ); + err.span_note(self.tcx.def_span(def_id), "opaque type is declared here"); + match self.defining_use_anchor { + DefiningAnchor::Bubble | DefiningAnchor::Error => {} + DefiningAnchor::Bind(bind) => { + err.span_note( + self.tcx.def_ident_span(bind).unwrap_or_else(|| self.tcx.def_span(bind)), + "this item depends on auto traits of the hidden type, \ + but may also be registering the hidden type. \ + This is not supported right now. \ + You can try moving the opaque type and the item that actually registers a hidden type into a new submodule".to_string(), + ); + } + }; + err + } + + fn report_type_parameter_mismatch_error( + &self, + obligation: &PredicateObligation<'tcx>, + span: Span, + found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { + let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref); + let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref); + + if expected_trait_ref.self_ty().references_error() { + return None; + } + + let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else { + return None; + }; + + let found_did = match *found_trait_ty.kind() { + ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) | ty::Generator(did, ..) => { + Some(did) + } + ty::Adt(def, _) => Some(def.did()), + _ => None, + }; + + let found_node = found_did.and_then(|did| self.tcx.hir().get_if_local(did)); + let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did)); + + if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { + // We check closures twice, with obligations flowing in different directions, + // but we want to complain about them only once. + return None; + } + + self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); + + let mut not_tupled = false; + + let found = match found_trait_ref.skip_binder().args.type_at(1).kind() { + ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], + _ => { + not_tupled = true; + vec![ArgKind::empty()] + } + }; + + let expected_ty = expected_trait_ref.skip_binder().args.type_at(1); + let expected = match expected_ty.kind() { + ty::Tuple(ref tys) => { + tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect() + } + _ => { + not_tupled = true; + vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())] + } + }; + + // If this is a `Fn` family trait and either the expected or found + // is not tupled, then fall back to just a regular mismatch error. + // This shouldn't be common unless manually implementing one of the + // traits manually, but don't make it more confusing when it does + // happen. + Some( + if Some(expected_trait_ref.def_id()) != self.tcx.lang_items().gen_trait() && not_tupled + { + self.report_and_explain_type_error( + TypeTrace::poly_trait_refs( + &obligation.cause, + true, + expected_trait_ref, + found_trait_ref, + ), + ty::error::TypeError::Mismatch, + ) + } else if found.len() == expected.len() { + self.report_closure_arg_mismatch( + span, + found_span, + found_trait_ref, + expected_trait_ref, + obligation.cause.code(), + found_node, + obligation.param_env, + ) + } else { + let (closure_span, closure_arg_span, found) = found_did + .and_then(|did| { + let node = self.tcx.hir().get_if_local(did)?; + let (found_span, closure_arg_span, found) = + self.get_fn_like_arguments(node)?; + Some((Some(found_span), closure_arg_span, found)) + }) + .unwrap_or((found_span, None, found)); + + self.report_arg_count_mismatch( + span, + closure_span, + expected, + found, + found_trait_ty.is_closure(), + closure_arg_span, + ) + }, + ) + } + + fn report_not_const_evaluatable_error( + &self, + obligation: &PredicateObligation<'tcx>, + span: Span, + ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { + if !self.tcx.features().generic_const_exprs { + let mut err = self + .tcx + .sess + .struct_span_err(span, "constant expression depends on a generic parameter"); + // FIXME(const_generics): we should suggest to the user how they can resolve this + // issue. However, this is currently not actually possible + // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). + // + // Note that with `feature(generic_const_exprs)` this case should not + // be reachable. + err.note("this may fail depending on what value the parameter takes"); + err.emit(); + return None; + } + + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { + let ty::ConstKind::Unevaluated(uv) = ct.kind() else { + bug!("const evaluatable failed for non-unevaluated const `{ct:?}`"); + }; + let mut err = self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); + let const_span = self.tcx.def_span(uv.def); + match self.tcx.sess.source_map().span_to_snippet(const_span) { + Ok(snippet) => err.help(format!( + "try adding a `where` bound using this expression: `where [(); {snippet}]:`" + )), + _ => err.help("consider adding a `where` bound using this expression"), + }; + Some(err) + } + _ => { + span_bug!( + span, + "unexpected non-ConstEvaluatable predicate, this should not be reachable" + ) + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 956f8e047d7..ab81d77a268 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -204,7 +204,7 @@ fn do_normalize_predicates<'tcx>( let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) { Ok(predicates) => predicates, Err(errors) => { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); + let reported = infcx.err_ctxt().report_fulfillment_errors(errors); return Err(reported); } }; diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index 6c49e94dc31..4b559632017 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -9,7 +9,7 @@ extern crate tracing; pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set}; pub mod layout; -pub(crate) mod maybe_transmutable; +mod maybe_transmutable; #[derive(Default)] pub struct Assume { @@ -19,7 +19,7 @@ pub struct Assume { pub validity: bool, } -/// Either we have an error, transmutation is allowed, or we have an optional +/// Either transmutation is allowed, we have an error, or we have an optional /// Condition that must hold. #[derive(Debug, Hash, Eq, PartialEq, Clone)] pub enum Answer<R> { diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index c0141f1f841..bf3c390c800 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -32,26 +32,6 @@ where ) -> Self { Self { src, dst, scope, assume, context } } - - // FIXME(bryangarza): Delete this when all usages are removed - pub(crate) fn map_layouts<F, M>( - self, - f: F, - ) -> Result<MaybeTransmutableQuery<M, C>, Answer<<C as QueryContext>::Ref>> - where - F: FnOnce( - L, - L, - <C as QueryContext>::Scope, - &C, - ) -> Result<(M, M), Answer<<C as QueryContext>::Ref>>, - { - let Self { src, dst, scope, assume, context } = self; - - let (src, dst) = f(src, dst, scope, &context)?; - - Ok(MaybeTransmutableQuery { src, dst, scope, assume, context }) - } } // FIXME: Nix this cfg, so we can write unit tests independently of rustc @@ -107,42 +87,42 @@ where #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))] pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> { let assume_visibility = self.assume.safety; - // FIXME(bryangarza): Refactor this code to get rid of `map_layouts` - let query_or_answer = self.map_layouts(|src, dst, scope, context| { - // Remove all `Def` nodes from `src`, without checking their visibility. - let src = src.prune(&|def| true); - trace!(?src, "pruned src"); + let Self { src, dst, scope, assume, context } = self; - // Remove all `Def` nodes from `dst`, additionally... - let dst = if assume_visibility { - // ...if visibility is assumed, don't check their visibility. - dst.prune(&|def| true) - } else { - // ...otherwise, prune away all unreachable paths through the `Dst` layout. - dst.prune(&|def| context.is_accessible_from(def, scope)) - }; + // Remove all `Def` nodes from `src`, without checking their visibility. + let src = src.prune(&|def| true); - trace!(?dst, "pruned dst"); + trace!(?src, "pruned src"); - // Convert `src` from a tree-based representation to an NFA-based representation. - // If the conversion fails because `src` is uninhabited, conclude that the transmutation - // is acceptable, because instances of the `src` type do not exist. - let src = Nfa::from_tree(src).map_err(|Uninhabited| Answer::Yes)?; + // Remove all `Def` nodes from `dst`, additionally... + let dst = if assume_visibility { + // ...if visibility is assumed, don't check their visibility. + dst.prune(&|def| true) + } else { + // ...otherwise, prune away all unreachable paths through the `Dst` layout. + dst.prune(&|def| context.is_accessible_from(def, scope)) + }; - // Convert `dst` from a tree-based representation to an NFA-based representation. - // If the conversion fails because `src` is uninhabited, conclude that the transmutation - // is unacceptable, because instances of the `dst` type do not exist. - let dst = - Nfa::from_tree(dst).map_err(|Uninhabited| Answer::No(Reason::DstIsPrivate))?; + trace!(?dst, "pruned dst"); - Ok((src, dst)) - }); + // Convert `src` from a tree-based representation to an NFA-based representation. + // If the conversion fails because `src` is uninhabited, conclude that the transmutation + // is acceptable, because instances of the `src` type do not exist. + let src = match Nfa::from_tree(src) { + Ok(src) => src, + Err(Uninhabited) => return Answer::Yes, + }; - match query_or_answer { - Ok(query) => query.answer(), - Err(answer) => answer, - } + // Convert `dst` from a tree-based representation to an NFA-based representation. + // If the conversion fails because `src` is uninhabited, conclude that the transmutation + // is unacceptable, because instances of the `dst` type do not exist. + let dst = match Nfa::from_tree(dst) { + Ok(dst) => dst, + Err(Uninhabited) => return Answer::No(Reason::DstIsPrivate), + }; + + MaybeTransmutableQuery { src, dst, scope, assume, context }.answer() } } @@ -156,14 +136,10 @@ where #[inline(always)] #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))] pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> { - // FIXME(bryangarza): Refactor this code to get rid of `map_layouts` - let query_or_answer = self - .map_layouts(|src, dst, scope, context| Ok((Dfa::from_nfa(src), Dfa::from_nfa(dst)))); - - match query_or_answer { - Ok(query) => query.answer(), - Err(answer) => answer, - } + let Self { src, dst, scope, assume, context } = self; + let src = Dfa::from_nfa(src); + let dst = Dfa::from_nfa(dst); + MaybeTransmutableQuery { src, dst, scope, assume, context }.answer() } } @@ -171,26 +147,8 @@ impl<C> MaybeTransmutableQuery<Dfa<<C as QueryContext>::Ref>, C> where C: QueryContext, { - /// Answers whether a `Nfa` is transmutable into another `Nfa`. - /// - /// This method converts `src` and `dst` to DFAs, then computes an answer using those DFAs. + /// Answers whether a `Dfa` is transmutable into another `Dfa`. pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> { - MaybeTransmutableQuery { - src: &self.src, - dst: &self.dst, - scope: self.scope, - assume: self.assume, - context: self.context, - } - .answer() - } -} - -impl<'l, C> MaybeTransmutableQuery<&'l Dfa<<C as QueryContext>::Ref>, C> -where - C: QueryContext, -{ - pub(crate) fn answer(&mut self) -> Answer<<C as QueryContext>::Ref> { self.answer_memo(&mut Map::default(), self.src.start, self.dst.start) } diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 9e4fdcda06e..fc8d19d1a58 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -479,6 +479,46 @@ pub trait Into<T>: Sized { /// - `From<T> for U` implies [`Into`]`<U> for T` /// - `From` is reflexive, which means that `From<T> for T` is implemented /// +/// # When to implement `From` +/// +/// While there's no technical restrictions on which conversions can be done using +/// a `From` implementation, the general expectation is that the conversions +/// should typically be restricted as follows: +/// +/// * The conversion is *infallible*: if the conversion can fail, use [`TryFrom`] +/// instead; don't provide a `From` impl that panics. +/// +/// * The conversion is *lossless*: semantically, it should not lose or discard +/// information. For example, `i32: From<u16>` exists, where the original +/// value can be recovered using `u16: TryFrom<i32>`. And `String: From<&str>` +/// exists, where you can get something equivalent to the original value via +/// `Deref`. But `From` cannot be used to convert from `u32` to `u16`, since +/// that cannot succeed in a lossless way. (There's some wiggle room here for +/// information not considered semantically relevant. For example, +/// `Box<[T]>: From<Vec<T>>` exists even though it might not preserve capacity, +/// like how two vectors can be equal despite differing capacities.) +/// +/// * The conversion is *value-preserving*: the conceptual kind and meaning of +/// the resulting value is the same, even though the Rust type and technical +/// representation might be different. For example `-1_i8 as u8` is *lossless*, +/// since `as` casting back can recover the original value, but that conversion +/// is *not* available via `From` because `-1` and `255` are different conceptual +/// values (despite being identical bit patterns technically). But +/// `f32: From<i16>` *is* available because `1_i16` and `1.0_f32` are conceptually +/// the same real number (despite having very different bit patterns technically). +/// `String: From<char>` is available because they're both *text*, but +/// `String: From<u32>` is *not* available, since `1` (a number) and `"1"` +/// (text) are too different. (Converting values to text is instead covered +/// by the [`Display`](crate::fmt::Display) trait.) +/// +/// * The conversion is *obvious*: it's the only reasonable conversion between +/// the two types. Otherwise it's better to have it be a named method or +/// constructor, like how [`str::as_bytes`] is a method and how integers have +/// methods like [`u32::from_ne_bytes`], [`u32::from_le_bytes`], and +/// [`u32::from_be_bytes`], none of which are `From` implementations. Whereas +/// there's only one reasonable way to wrap an [`Ipv6Addr`](crate::net::Ipv6Addr) +/// into an [`IpAddr`](crate::net::IpAddr), thus `IpAddr: From<Ipv6Addr>` exists. +/// /// # Examples /// /// [`String`] implements `From<&str>`: diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 9899014e85a..bdaeea66622 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1475,7 +1475,7 @@ impl<T> Option<T> { #[stable(feature = "rust1", since = "1.0.0")] pub fn or(self, optb: Option<T>) -> Option<T> { match self { - Some(x) => Some(x), + x @ Some(_) => x, None => optb, } } @@ -1500,7 +1500,7 @@ impl<T> Option<T> { F: FnOnce() -> Option<T>, { match self { - Some(x) => Some(x), + x @ Some(_) => x, None => f(), } } @@ -1530,8 +1530,8 @@ impl<T> Option<T> { #[stable(feature = "option_xor", since = "1.37.0")] pub fn xor(self, optb: Option<T>) -> Option<T> { match (self, optb) { - (Some(a), None) => Some(a), - (None, Some(b)) => Some(b), + (a @ Some(_), None) => a, + (None, b @ Some(_)) => b, _ => None, } } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 965132bdedb..feb6274d029 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -36,6 +36,10 @@ object = { version = "0.32.0", default-features = false, optional = true, featur rand = { version = "0.8.5", default-features = false, features = ["alloc"] } rand_xorshift = "0.3.0" +[build-dependencies] +# Dependency of the `backtrace` crate's build script +cc = "1.0.67" + [target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] } diff --git a/library/std/build.rs b/library/std/build.rs index 36516978b7a..164bca7c436 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -1,5 +1,11 @@ use std::env; +// backtrace-rs requires a feature check on Android targets, so +// we need to run its build.rs as well. +#[allow(unused_extern_crates)] +#[path = "../backtrace/build.rs"] +mod backtrace_build_rs; + fn main() { println!("cargo:rerun-if-changed=build.rs"); let target = env::var("TARGET").expect("TARGET was not set"); @@ -58,4 +64,6 @@ fn main() { } println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap()); println!("cargo:rustc-cfg=backtrace_in_libstd"); + + backtrace_build_rs::main(); } diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 8c46080e43b..2bb4f3f9e03 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -125,7 +125,7 @@ impl Once { /// /// # Panics /// - /// The closure `f` will only be executed once if this is called + /// The closure `f` will only be executed once even if this is called /// concurrently amongst many threads. If that closure panics, however, then /// it will *poison* this [`Once`] instance, causing all future invocations of /// `call_once` to also panic. diff --git a/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs index e9d19766592..4a732a2cabd 100644 --- a/library/std/src/sys/unix/thread_local_dtor.rs +++ b/library/std/src/sys/unix/thread_local_dtor.rs @@ -67,7 +67,7 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { // workaround below is to register, via _tlv_atexit, a custom DTOR list once per // thread. thread_local dtors are pushed to the DTOR list without calling // _tlv_atexit. -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::cell::Cell; use crate::mem; diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 8c9ab1e2402..fef25ad8635 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,101 @@ document. ## Unreleased / Beta / In Rust Nightly -[37f4c172...master](https://github.com/rust-lang/rust-clippy/compare/37f4c172...master) +[1e8fdf49...master](https://github.com/rust-lang/rust-clippy/compare/1e8fdf49...master) + +## Rust 1.73 + +Current stable, released 2023-10-05 + +[View all 103 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-07-02T12%3A24%3A40Z..2023-08-11T11%3A09%3A56Z+base%3Amaster) + +### New Lints + +* [`impossible_comparisons`] + [#10843](https://github.com/rust-lang/rust-clippy/pull/10843) +* [`redundant_comparisons`] + [#10843](https://github.com/rust-lang/rust-clippy/pull/10843) +* [`ignored_unit_patterns`] + [#11242](https://github.com/rust-lang/rust-clippy/pull/11242) +* [`readonly_write_lock`] + [#11210](https://github.com/rust-lang/rust-clippy/pull/11210) +* [`filter_map_bool_then`] + [#11115](https://github.com/rust-lang/rust-clippy/pull/11115) +* [`needless_return_with_question_mark`] + [#11031](https://github.com/rust-lang/rust-clippy/pull/11031) +* [`redundant_guards`] + [#10955](https://github.com/rust-lang/rust-clippy/pull/10955) +* [`redundant_locals`] + [#10885](https://github.com/rust-lang/rust-clippy/pull/10885) +* [`absolute_paths`] + [#11003](https://github.com/rust-lang/rust-clippy/pull/11003) +* [`error_impl_error`] + [#11107](https://github.com/rust-lang/rust-clippy/pull/11107) +* [`iter_skip_zero`] + [#11046](https://github.com/rust-lang/rust-clippy/pull/11046) +* [`string_lit_chars_any`] + [#11052](https://github.com/rust-lang/rust-clippy/pull/11052) +* [`four_forward_slashes`] + [#11140](https://github.com/rust-lang/rust-clippy/pull/11140) +* [`format_collect`] + [#11116](https://github.com/rust-lang/rust-clippy/pull/11116) +* [`needless_pass_by_ref_mut`] + [#10900](https://github.com/rust-lang/rust-clippy/pull/10900) +* [`manual_is_infinite`] + [#11049](https://github.com/rust-lang/rust-clippy/pull/11049) +* [`manual_is_finite`] + [#11049](https://github.com/rust-lang/rust-clippy/pull/11049) +* [`incorrect_partial_ord_impl_on_ord_type`] + [#10788](https://github.com/rust-lang/rust-clippy/pull/10788) +* [`read_line_without_trim`] + [#10970](https://github.com/rust-lang/rust-clippy/pull/10970) +* [`type_id_on_box`] + [#10987](https://github.com/rust-lang/rust-clippy/pull/10987) + +### Moves and Deprecations + +* Renamed `unwrap_or_else_default` to [`unwrap_or_default`] + [#10120](https://github.com/rust-lang/rust-clippy/pull/10120) +* Moved [`tuple_array_conversions`] to `pedantic` (Now allow-by-default) + [#11146](https://github.com/rust-lang/rust-clippy/pull/11146) +* Moved [`arc_with_non_send_sync`] to `suspicious` (Now warn-by-default) + [#11104](https://github.com/rust-lang/rust-clippy/pull/11104) +* Moved [`needless_raw_string_hashes`] to `pedantic` (Now allow-by-default) + [#11415](https://github.com/rust-lang/rust-clippy/pull/11415) + +### Enhancements + +* [`unwrap_used`]: No longer lints on the never-type or never-like enums + [#11252](https://github.com/rust-lang/rust-clippy/pull/11252) +* [`expect_used`]: No longer lints on the never-type or never-like enums + [#11252](https://github.com/rust-lang/rust-clippy/pull/11252) + +### False Positive Fixes + +* [`panic_in_result_fn`]: No longer triggers on `todo!`, `unimplemented!`, `unreachable!` + [#11123](https://github.com/rust-lang/rust-clippy/pull/11123) + +### Suggestion Fixes/Improvements + +* [`semicolon_if_nothing_returned`]: The suggestion is now machine-applicable with rustfix + [#11083](https://github.com/rust-lang/rust-clippy/pull/11083) + +### ICE Fixes + +* [`filter_map_bool_then`]: No longer crashes on late-bound regions + [#11318](https://github.com/rust-lang/rust-clippy/pull/11318) +* [`unwrap_or_default`]: No longer crashes on alias types for local items + [#11258](https://github.com/rust-lang/rust-clippy/pull/11258) +* [`unnecessary_literal_unwrap`]: No longer crashes on `None.unwrap_or_default()` + [#11106](https://github.com/rust-lang/rust-clippy/pull/11106) +* Fixed MIR-related ICE + [#11130](https://github.com/rust-lang/rust-clippy/pull/11130) +* [`missing_fields_in_debug`]: No longer crashes on non-ADT self types + [#11069](https://github.com/rust-lang/rust-clippy/pull/11069) ## Rust 1.72 -Current stable, released 2023-08-24 +Released 2023-08-24 [View all 131 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-05-22T14%3A53%3A59Z..2023-07-01T22%3A57%3A20Z+base%3Amaster) @@ -5011,6 +5101,7 @@ Released 2018-09-13 [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division [`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref +[`into_iter_without_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_without_iter [`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering [`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage [`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref @@ -5036,6 +5127,7 @@ Released 2018-09-13 [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next [`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero [`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain +[`iter_without_into_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_without_into_iter [`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero [`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits [`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays @@ -5072,6 +5164,7 @@ Released 2018-09-13 [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten +[`manual_hash_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one [`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check [`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 66786004f6e..cbcb42dad79 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.74" +version = "0.1.75" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -25,6 +25,8 @@ clippy_lints = { path = "clippy_lints" } rustc_tools_util = "0.3.0" tempfile = { version = "3.2", optional = true } termize = "0.1" +color-print = "0.3.4" # Sync version with Cargo +anstream = "0.5.0" [dev-dependencies] ui_test = "0.20" diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index f6f0c95c729..e001197842b 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -261,7 +261,7 @@ impl EarlyLintPass for FooFunctions {} [declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60 [example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints -[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110 +[category_level_mapping]: ../index.html ## Lint registration diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index b980083f1f5..2c958ccbbc2 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -151,6 +151,7 @@ The minimum rust version that the project supports * [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) * [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold) +* [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one) ## `cognitive-complexity-threshold` diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index e64cf2c8749..be2386bb1d2 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -58,7 +58,7 @@ pub fn create( }; create_lint(&lint, msrv).context("Unable to create lint implementation")?; - create_test(&lint).context("Unable to create a test for the new lint")?; + create_test(&lint, msrv).context("Unable to create a test for the new lint")?; if lint.ty.is_none() { add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?; @@ -88,15 +88,21 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { } } -fn create_test(lint: &LintData<'_>) -> io::Result<()> { - fn create_project_layout<P: Into<PathBuf>>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> { +fn create_test(lint: &LintData<'_>, msrv: bool) -> io::Result<()> { + fn create_project_layout<P: Into<PathBuf>>( + lint_name: &str, + location: P, + case: &str, + hint: &str, + msrv: bool, + ) -> io::Result<()> { let mut path = location.into().join(case); fs::create_dir(&path)?; write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?; path.push("src"); fs::create_dir(&path)?; - write_file(path.join("main.rs"), get_test_file_contents(lint_name))?; + write_file(path.join("main.rs"), get_test_file_contents(lint_name, msrv))?; Ok(()) } @@ -106,13 +112,25 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> { let test_dir = lint.project_root.join(&relative_test_dir); fs::create_dir(&test_dir)?; - create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?; - create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")?; + create_project_layout( + lint.name, + &test_dir, + "fail", + "Content that triggers the lint goes here", + msrv, + )?; + create_project_layout( + lint.name, + &test_dir, + "pass", + "This file should not trigger the lint", + false, + )?; println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`"); } else { let test_path = format!("tests/ui/{}.rs", lint.name); - let test_contents = get_test_file_contents(lint.name); + let test_contents = get_test_file_contents(lint.name, msrv); write_file(lint.project_root.join(&test_path), test_contents)?; println!("Generated test file: `{test_path}`"); @@ -194,8 +212,8 @@ pub(crate) fn get_stabilization_version() -> String { parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`") } -fn get_test_file_contents(lint_name: &str) -> String { - formatdoc!( +fn get_test_file_contents(lint_name: &str, msrv: bool) -> String { + let mut test = formatdoc!( r#" #![warn(clippy::{lint_name})] @@ -203,7 +221,29 @@ fn get_test_file_contents(lint_name: &str) -> String { // test code goes here }} "# - ) + ); + + if msrv { + let _ = writedoc!( + test, + r#" + + // TODO: set xx to the version one below the MSRV used by the lint, and yy to + // the version used by the lint + #[clippy::msrv = "1.xx"] + fn msrv_1_xx() {{ + // a simple example that would trigger the lint if the MSRV were met + }} + + #[clippy::msrv = "1.yy"] + fn msrv_1_yy() {{ + // the same example as above + }} + "# + ); + } + + test } fn get_manifest_contents(lint_name: &str, hint: &str) -> String { @@ -258,7 +298,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { ) }); - let _: fmt::Result = write!(result, "{}", get_lint_declaration(&name_upper, category)); + let _: fmt::Result = writeln!(result, "{}", get_lint_declaration(&name_upper, category)); result.push_str(&if enable_msrv { formatdoc!( @@ -281,7 +321,6 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { }} // 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` "# ) diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index dcd9a4adcbd..4d5b3bf8a94 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.74" +version = "0.1.75" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -28,6 +28,9 @@ semver = "1.0" rustc-semver = "1.1" url = "2.2" +[dev-dependencies] +walkdir = "2.3" + [features] deny-warnings = ["clippy_utils/deny-warnings"] # build clippy with internal lints enabled, off by default diff --git a/src/tools/clippy/clippy_lints/src/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/allow_attributes.rs index e1ef514edfd..e3f4cf79d31 100644 --- a/src/tools/clippy/clippy_lints/src/allow_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/allow_attributes.rs @@ -8,6 +8,7 @@ use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { + /// ### What it does /// Checks for usage of the `#[allow]` attribute and suggests replacing it with /// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) /// @@ -19,7 +20,6 @@ declare_clippy_lint! { /// (`#![allow]`) are usually used to enable or disable lints on a global scale. /// /// ### Why is this bad? - /// /// `#[expect]` attributes suppress the lint emission, but emit a warning, if /// the expectation is unfulfilled. This can be useful to be notified when the /// lint is no longer triggered. diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs index 0546807bac4..db01ddbde04 100644 --- a/src/tools/clippy/clippy_lints/src/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -183,7 +183,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for empty lines after documenation comments. + /// Checks for empty lines after documentation comments. /// /// ### Why is this bad? /// The documentation comment was most likely meant to be an inner attribute or regular comment. @@ -795,7 +795,7 @@ impl EarlyLintPass for EarlyAttributes { /// Check for empty lines after outer attributes. /// -/// Attributes and documenation comments are both considered outer attributes +/// Attributes and documentation comments are both considered outer attributes /// by the AST. However, the average user likely considers them to be different. /// Checking for empty lines after each of these attributes is split into two different /// lints but can share the same logic. diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 4d1281ec1e7..481c44031cf 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -229,6 +229,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO, crate::items_after_test_module::ITEMS_AFTER_TEST_MODULE_INFO, crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO, + crate::iter_without_into_iter::INTO_ITER_WITHOUT_ITER_INFO, + crate::iter_without_into_iter::ITER_WITHOUT_INTO_ITER_INFO, crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO, crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO, crate::large_futures::LARGE_FUTURES_INFO, @@ -280,6 +282,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::manual_clamp::MANUAL_CLAMP_INFO, crate::manual_float_methods::MANUAL_IS_FINITE_INFO, crate::manual_float_methods::MANUAL_IS_INFINITE_INFO, + crate::manual_hash_one::MANUAL_HASH_ONE_INFO, crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO, diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs index 3f60e5a7c4d..646767868e2 100644 --- a/src/tools/clippy/clippy_lints/src/enum_clike.rs +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -1,7 +1,7 @@ //! lint on C-like enums that are `repr(isize/usize)` and have values that //! don't fit into an `i32` -use clippy_utils::consts::{miri_to_const, Constant}; +use clippy_utils::consts::{mir_to_const, Constant}; use clippy_utils::diagnostics::span_lint; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { .const_eval_poly(def_id.to_def_id()) .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty)); - if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx, c)) { + if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx, c)) { if let ty::Adt(adt, _) = ty.kind() { if adt.is_enum() { ty = adt.repr().discr_type().to_ty(cx.tcx); diff --git a/src/tools/clippy/clippy_lints/src/error_impl_error.rs b/src/tools/clippy/clippy_lints/src/error_impl_error.rs index f24577c7382..6d429fbd035 100644 --- a/src/tools/clippy/clippy_lints/src/error_impl_error.rs +++ b/src/tools/clippy/clippy_lints/src/error_impl_error.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// /// impl std::error::Error for Error { ... } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub ERROR_IMPL_ERROR, restriction, "exported types named `Error` that implement `Error`" diff --git a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs index 419c7734344..0ec52f89e71 100644 --- a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs +++ b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs @@ -28,7 +28,7 @@ declare_clippy_lint! { /// // ... /// } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub FOUR_FORWARD_SLASHES, suspicious, "comments with 4 forward slashes (`////`) likely intended to be doc comments (`///`)" diff --git a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs index d8ead1c9d9f..ef2a66d4a20 100644 --- a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs @@ -37,6 +37,10 @@ declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]); impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) { + if pat.span.from_expansion() { + return; + } + match cx.tcx.hir().get_parent(pat.hir_id) { Node::Param(param) if matches!(cx.tcx.hir().get_parent(param.hir_id), Node::Item(_)) => { // Ignore function parameters diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 64a4a3fa741..2b2ea156cd4 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -6,9 +6,8 @@ use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; use rustc_hir_analysis::hir_ty_to_ty; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -162,7 +161,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { vis.visit_ty(ty); for target in &vis.found { - if in_external_macro(cx.sess(), generics.span) { + if generics.span.from_expansion() { continue; } let generics_suggestion_span = generics.span.substitute_dummy({ diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs index 55a43e91562..41477242bcc 100644 --- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs +++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs @@ -1,10 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{is_from_proc_macro, is_in_cfg_test}; -use rustc_hir::{HirId, ItemId, ItemKind, Mod}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::source::snippet_opt; +use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro}; +use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_hir::{HirId, Item, ItemKind, Mod}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{sym, Span}; +use rustc_span::hygiene::AstPass; +use rustc_span::{sym, ExpnKind}; declare_clippy_lint! { /// ### What it does @@ -41,46 +43,72 @@ declare_clippy_lint! { declare_lint_pass!(ItemsAfterTestModule => [ITEMS_AFTER_TEST_MODULE]); -impl LateLintPass<'_> for ItemsAfterTestModule { - fn check_mod(&mut self, cx: &LateContext<'_>, _: &Mod<'_>, _: HirId) { - let mut was_test_mod_visited = false; - let mut test_mod_span: Option<Span> = None; +fn cfg_test_module<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool { + if let ItemKind::Mod(test_mod) = item.kind + && item.span.hi() == test_mod.spans.inner_span.hi() + && is_cfg_test(cx.tcx, item.hir_id()) + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + { + true + } else { + false + } +} - let hir = cx.tcx.hir(); - let items = hir.items().collect::<Vec<ItemId>>(); +impl LateLintPass<'_> for ItemsAfterTestModule { + fn check_mod(&mut self, cx: &LateContext<'_>, module: &Mod<'_>, _: HirId) { + let mut items = module.item_ids.iter().map(|&id| cx.tcx.hir().item(id)); - for (i, itid) in items.iter().enumerate() { - let item = hir.item(*itid); + let Some((mod_pos, test_mod)) = items.by_ref().enumerate().find(|(_, item)| cfg_test_module(cx, item)) else { + return; + }; - if_chain! { - if was_test_mod_visited; - if i == (items.len() - 3 /* Weird magic number (HIR-translation behaviour) */); - if cx.sess().source_map().lookup_char_pos(item.span.lo()).file.name_hash - == cx.sess().source_map().lookup_char_pos(test_mod_span.unwrap().lo()).file.name_hash; // Will never fail - if !matches!(item.kind, ItemKind::Mod(_)); - if !is_in_cfg_test(cx.tcx, itid.hir_id()); // The item isn't in the testing module itself - if !in_external_macro(cx.sess(), item.span); - if !is_from_proc_macro(cx, item); + let after: Vec<_> = items + .filter(|item| { + // Ignore the generated test main function + !(item.ident.name == sym::main + && item.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::TestHarness)) + }) + .collect(); - then { - span_lint_and_help(cx, ITEMS_AFTER_TEST_MODULE, test_mod_span.unwrap().with_hi(item.span.hi()), "items were found after the testing module", None, "move the items to before the testing module was defined"); - }}; + if let Some(last) = after.last() + && after.iter().all(|&item| { + !matches!(item.kind, ItemKind::Mod(_)) + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + }) + && !fulfill_or_allowed(cx, ITEMS_AFTER_TEST_MODULE, after.iter().map(|item| item.hir_id())) + { + let def_spans: Vec<_> = std::iter::once(test_mod.owner_id) + .chain(after.iter().map(|item| item.owner_id)) + .map(|id| cx.tcx.def_span(id)) + .collect(); - if let ItemKind::Mod(module) = item.kind && item.span.hi() == module.spans.inner_span.hi() { - // Check that it works the same way, the only I way I've found for #10713 - for attr in cx.tcx.get_attrs(item.owner_id.to_def_id(), sym::cfg) { - if_chain! { - if attr.has_name(sym::cfg); - if let Some(mitems) = attr.meta_item_list(); - if let [mitem] = &*mitems; - if mitem.has_name(sym::test); - then { - was_test_mod_visited = true; - test_mod_span = Some(item.span); - } + span_lint_hir_and_then( + cx, + ITEMS_AFTER_TEST_MODULE, + test_mod.hir_id(), + def_spans, + "items after a test module", + |diag| { + if let Some(prev) = mod_pos.checked_sub(1) + && let prev = cx.tcx.hir().item(module.item_ids[prev]) + && let items_span = last.span.with_lo(test_mod.span.hi()) + && let Some(items) = snippet_opt(cx, items_span) + { + diag.multipart_suggestion_with_style( + "move the items to before the test module was defined", + vec![ + (prev.span.shrink_to_hi(), items), + (items_span, String::new()) + ], + Applicability::MachineApplicable, + SuggestionStyle::HideCodeAlways, + ); } - } - } + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs new file mode 100644 index 00000000000..43e1b92c9b9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -0,0 +1,249 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_parent_as_impl; +use clippy_utils::source::snippet; +use clippy_utils::ty::{implements_trait, make_normalized_projection}; +use rustc_ast::Mutability; +use rustc_errors::Applicability; +use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, Symbol}; + +declare_clippy_lint! { + /// ### What it does + /// Looks for `iter` and `iter_mut` methods without an associated `IntoIterator for (&|&mut) Type` implementation. + /// + /// ### Why is this bad? + /// It's not bad, but having them is idiomatic and allows the type to be used in for loops directly + /// (`for val in &iter {}`), without having to first call `iter()` or `iter_mut()`. + /// + /// ### Example + /// ```rust + /// struct MySlice<'a>(&'a [u8]); + /// impl<'a> MySlice<'a> { + /// pub fn iter(&self) -> std::slice::Iter<'a, u8> { + /// self.0.iter() + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct MySlice<'a>(&'a [u8]); + /// impl<'a> MySlice<'a> { + /// pub fn iter(&self) -> std::slice::Iter<'a, u8> { + /// self.0.iter() + /// } + /// } + /// impl<'a> IntoIterator for &MySlice<'a> { + /// type Item = &'a u8; + /// type IntoIter = std::slice::Iter<'a, u8>; + /// fn into_iter(self) -> Self::IntoIter { + /// self.iter() + /// } + /// } + /// ``` + #[clippy::version = "1.74.0"] + pub ITER_WITHOUT_INTO_ITER, + pedantic, + "implementing `iter(_mut)` without an associated `IntoIterator for (&|&mut) Type` impl" +} + +declare_clippy_lint! { + /// ### What it does + /// This is the opposite of the `iter_without_into_iter` lint. + /// It looks for `IntoIterator for (&|&mut) Type` implementations without an inherent `iter` or `iter_mut` method. + /// + /// ### Why is this bad? + /// It's not bad, but having them is idiomatic and allows the type to be used in iterator chains + /// by just calling `.iter()`, instead of the more awkward `<&Type>::into_iter` or `(&val).iter()` syntax + /// in case of ambiguity with another `Intoiterator` impl. + /// + /// ### Example + /// ```rust + /// struct MySlice<'a>(&'a [u8]); + /// impl<'a> IntoIterator for &MySlice<'a> { + /// type Item = &'a u8; + /// type IntoIter = std::slice::Iter<'a, u8>; + /// fn into_iter(self) -> Self::IntoIter { + /// self.0.iter() + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct MySlice<'a>(&'a [u8]); + /// impl<'a> MySlice<'a> { + /// pub fn iter(&self) -> std::slice::Iter<'a, u8> { + /// self.into_iter() + /// } + /// } + /// impl<'a> IntoIterator for &MySlice<'a> { + /// type Item = &'a u8; + /// type IntoIter = std::slice::Iter<'a, u8>; + /// fn into_iter(self) -> Self::IntoIter { + /// self.0.iter() + /// } + /// } + /// ``` + #[clippy::version = "1.74.0"] + pub INTO_ITER_WITHOUT_ITER, + pedantic, + "implementing `IntoIterator for (&|&mut) Type` without an inherent `iter(_mut)` method" +} + +declare_lint_pass!(IterWithoutIntoIter => [ITER_WITHOUT_INTO_ITER, INTO_ITER_WITHOUT_ITER]); + +/// Checks if a given type is nameable in a trait (impl). +/// RPIT is stable, but impl Trait in traits is not (yet), so when we have +/// a function such as `fn iter(&self) -> impl IntoIterator`, we can't +/// suggest `type IntoIter = impl IntoIterator`. +fn is_nameable_in_impl_trait(ty: &rustc_hir::Ty<'_>) -> bool { + !matches!(ty.kind, TyKind::OpaqueDef(..)) +} + +fn type_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { + if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) { + cx.tcx.inherent_impls(ty_did).iter().any(|&did| { + cx.tcx + .associated_items(did) + .filter_by_name_unhygienic(method_name) + .next() + .is_some_and(|item| item.kind == ty::AssocKind::Fn) + }) + } else { + false + } +} + +impl LateLintPass<'_> for IterWithoutIntoIter { + fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) { + if let ItemKind::Impl(imp) = item.kind + && let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind + && let Some(trait_ref) = imp.of_trait + && trait_ref.trait_def_id().is_some_and(|did| cx.tcx.is_diagnostic_item(sym::IntoIterator, did)) + && let &ty::Ref(_, ty, mtbl) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind() + && let expected_method_name = match mtbl { + Mutability::Mut => sym::iter_mut, + Mutability::Not => sym::iter, + } + && !type_has_inherent_method(cx, ty, expected_method_name) + && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| { + if item.ident.name == sym!(IntoIter) { + Some(cx.tcx.hir().impl_item(item.id).expect_type().span) + } else { + None + } + }) + { + span_lint_and_then( + cx, + INTO_ITER_WITHOUT_ITER, + item.span, + &format!("`IntoIterator` implemented for a reference type without an `{expected_method_name}` method"), + |diag| { + // The suggestion forwards to the `IntoIterator` impl and uses a form of UFCS + // to avoid name ambiguities, as there might be an inherent into_iter method + // that we don't want to call. + let sugg = format!( +" +impl {self_ty_without_ref} {{ + fn {expected_method_name}({ref_self}self) -> {iter_ty} {{ + <{ref_self}Self as IntoIterator>::into_iter(self) + }} +}} +", + self_ty_without_ref = snippet(cx, self_ty_without_ref.ty.span, ".."), + ref_self = mtbl.ref_prefix_str(), + iter_ty = snippet(cx, iter_assoc_span, ".."), + ); + + diag.span_suggestion_verbose( + item.span.shrink_to_lo(), + format!("consider implementing `{expected_method_name}`"), + sugg, + // Just like iter_without_into_iter, this suggestion is on a best effort basis + // and requires potentially adding lifetimes or moving them around. + Applicability::Unspecified + ); + } + ); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) { + let item_did = item.owner_id.to_def_id(); + let (borrow_prefix, expected_implicit_self) = match item.ident.name { + sym::iter => ("&", ImplicitSelfKind::ImmRef), + sym::iter_mut => ("&mut ", ImplicitSelfKind::MutRef), + _ => return, + }; + + if let ImplItemKind::Fn(sig, _) = item.kind + && let FnRetTy::Return(ret) = sig.decl.output + && is_nameable_in_impl_trait(ret) + && cx.tcx.generics_of(item_did).params.is_empty() + && sig.decl.implicit_self == expected_implicit_self + && sig.decl.inputs.len() == 1 + && let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()) + && imp.of_trait.is_none() + && let sig = cx.tcx.liberate_late_bound_regions( + item_did, + cx.tcx.fn_sig(item_did).instantiate_identity() + ) + && let ref_ty = sig.inputs()[0] + && let Some(into_iter_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator) + && let Some(iterator_did) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let ret_ty = sig.output() + // Order is important here, we need to check that the `fn iter` return type actually implements `IntoIterator` + // *before* normalizing `<_ as IntoIterator>::Item` (otherwise make_normalized_projection ICEs) + && implements_trait(cx, ret_ty, iterator_did, &[]) + && let Some(iter_ty) = make_normalized_projection( + cx.tcx, + cx.param_env, + iterator_did, + sym!(Item), + [ret_ty], + ) + // Only lint if the `IntoIterator` impl doesn't actually exist + && !implements_trait(cx, ref_ty, into_iter_did, &[]) + { + let self_ty_snippet = format!("{borrow_prefix}{}", snippet(cx, imp.self_ty.span, "..")); + + span_lint_and_then( + cx, + ITER_WITHOUT_INTO_ITER, + item.span, + &format!("`{}` method without an `IntoIterator` impl for `{self_ty_snippet}`", item.ident), + |diag| { + // Get the lower span of the `impl` block, and insert the suggestion right before it: + // impl X { + // ^ fn iter(&self) -> impl IntoIterator { ... } + // } + let span_behind_impl = cx.tcx + .def_span(cx.tcx.hir().parent_id(item.hir_id()).owner.def_id) + .shrink_to_lo(); + + let sugg = format!( +" +impl IntoIterator for {self_ty_snippet} {{ + type IntoIter = {ret_ty}; + type Iter = {iter_ty}; + fn into_iter() -> Self::IntoIter {{ + self.iter() + }} +}} +" + ); + diag.span_suggestion_verbose( + span_behind_impl, + format!("consider implementing `IntoIterator` for `{self_ty_snippet}`"), + sugg, + // Suggestion is on a best effort basis, might need some adjustments by the user + // such as adding some lifetimes in the associated types, or importing types. + Applicability::Unspecified, + ); + }); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 1271be2fd93..0f35ec27665 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -169,6 +169,7 @@ mod invalid_upcast_comparisons; mod items_after_statements; mod items_after_test_module; mod iter_not_returning_iterator; +mod iter_without_into_iter; mod large_const_arrays; mod large_enum_variant; mod large_futures; @@ -190,6 +191,7 @@ mod manual_async_fn; mod manual_bits; mod manual_clamp; mod manual_float_methods; +mod manual_hash_one; mod manual_is_ascii_check; mod manual_let_else; mod manual_main_separator_str; @@ -1119,6 +1121,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: msrv(), )) }); + store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(msrv()))); + store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs index 88db7ae6aec..ed9189a1804 100644 --- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs +++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// # let x = 1.0f32; /// if x.is_infinite() {} /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub MANUAL_IS_INFINITE, style, "use dedicated method to check if a float is infinite" @@ -51,7 +51,7 @@ declare_clippy_lint! { /// if x.is_finite() {} /// if x.is_finite() {} /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub MANUAL_IS_FINITE, style, "use dedicated method to check if a float is finite" diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs new file mode 100644 index 00000000000..ea911335450 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -0,0 +1,133 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_opt; +use clippy_utils::visitors::{is_local_used, local_used_once}; +use clippy_utils::{is_trait_method, path_to_local_id}; +use rustc_errors::Applicability; +use rustc_hir::{BindingAnnotation, ExprKind, Local, Node, PatKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for cases where [`BuildHasher::hash_one`] can be used. + /// + /// [`BuildHasher::hash_one`]: https://doc.rust-lang.org/std/hash/trait.BuildHasher.html#method.hash_one + /// + /// ### Why is this bad? + /// It is more concise to use the `hash_one` method. + /// + /// ### Example + /// ```rust + /// use std::hash::{BuildHasher, Hash, Hasher}; + /// use std::collections::hash_map::RandomState; + /// + /// let s = RandomState::new(); + /// let value = vec![1, 2, 3]; + /// + /// let mut hasher = s.build_hasher(); + /// value.hash(&mut hasher); + /// let hash = hasher.finish(); + /// ``` + /// Use instead: + /// ```rust + /// use std::hash::BuildHasher; + /// use std::collections::hash_map::RandomState; + /// + /// let s = RandomState::new(); + /// let value = vec![1, 2, 3]; + /// + /// let hash = s.hash_one(&value); + /// ``` + #[clippy::version = "1.74.0"] + pub MANUAL_HASH_ONE, + complexity, + "manual implementations of `BuildHasher::hash_one`" +} + +pub struct ManualHashOne { + msrv: Msrv, +} + +impl ManualHashOne { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualHashOne => [MANUAL_HASH_ONE]); + +impl LateLintPass<'_> for ManualHashOne { + fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { + // `let mut hasher = seg.build_hasher();` + if let PatKind::Binding(BindingAnnotation::MUT, hasher, _, None) = local.pat.kind + && let Some(init) = local.init + && !init.span.from_expansion() + && let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind + && seg.ident.name == sym!(build_hasher) + + && let Node::Stmt(local_stmt) = cx.tcx.hir().get_parent(local.hir_id) + && let Node::Block(block) = cx.tcx.hir().get_parent(local_stmt.hir_id) + + && let mut stmts = block.stmts.iter() + .skip_while(|stmt| stmt.hir_id != local_stmt.hir_id) + .skip(1) + .filter(|&stmt| is_local_used(cx, stmt, hasher)) + + // `hashed_value.hash(&mut hasher);` + && let Some(hash_stmt) = stmts.next() + && let StmtKind::Semi(hash_expr) = hash_stmt.kind + && !hash_expr.span.from_expansion() + && let ExprKind::MethodCall(seg, hashed_value, [ref_to_hasher], _) = hash_expr.kind + && seg.ident.name == sym::hash + && is_trait_method(cx, hash_expr, sym::Hash) + && path_to_local_id(ref_to_hasher.peel_borrows(), hasher) + + && let maybe_finish_stmt = stmts.next() + // There should be no more statements referencing `hasher` + && stmts.next().is_none() + + // `hasher.finish()`, may be anywhere in a statement or the trailing expr of the block + && let Some(path_expr) = local_used_once(cx, (maybe_finish_stmt, block.expr), hasher) + && let Node::Expr(finish_expr) = cx.tcx.hir().get_parent(path_expr.hir_id) + && !finish_expr.span.from_expansion() + && let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind + && seg.ident.name == sym!(finish) + + && self.msrv.meets(msrvs::BUILD_HASHER_HASH_ONE) + { + span_lint_hir_and_then( + cx, + MANUAL_HASH_ONE, + finish_expr.hir_id, + finish_expr.span, + "manual implementation of `BuildHasher::hash_one`", + |diag| { + if let Some(build_hasher) = snippet_opt(cx, build_hasher.span) + && let Some(hashed_value) = snippet_opt(cx, hashed_value.span) + { + diag.multipart_suggestion( + "try", + vec![ + (local_stmt.span, String::new()), + (hash_stmt.span, String::new()), + ( + finish_expr.span, + // `needless_borrows_for_generic_args` will take care of + // removing the `&` when it isn't needed + format!("{build_hasher}.hash_one(&{hashed_value})") + ) + ], + Applicability::MachineApplicable, + ); + + } + }, + ); + } + } + + extract_msrv_attr!(LateContext); +} diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index c531137b785..2117308cd40 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -136,9 +136,9 @@ fn emit_manual_let_else( // for this to be machine applicable. let mut app = Applicability::HasPlaceholders; let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app); - let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app); + let (sn_else, else_is_mac_call) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app); - let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) { + let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) && !else_is_mac_call { sn_else.into_owned() } else { format!("{{ {sn_else} }}") diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 0e22485db2c..c12727c4a28 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -3,6 +3,7 @@ use clippy_utils::is_doc_hidden; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{self, VisibilityKind}; +use rustc_ast::attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -158,7 +159,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { let mut iter = def.variants.iter().filter_map(|v| { (matches!(v.data, hir::VariantData::Unit(_, _)) && v.ident.as_str().starts_with('_') - && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))) + && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id)) + && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive)) .then_some((v.def_id, v.span)) }); if let Some((id, span)) = iter.next() @@ -198,16 +200,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { enum_span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive() - && let header_span = cx.sess().source_map().span_until_char(enum_span, '{') - && let Some(snippet) = snippet_opt(cx, header_span) - { - diag.span_suggestion( - header_span, - "add the attribute", - format!("#[non_exhaustive] {snippet}"), - Applicability::Unspecified, - ); + let header_span = cx.sess().source_map().span_until_char(enum_span, '{'); + if let Some(snippet) = snippet_opt(cx, header_span) { + diag.span_suggestion( + header_span, + "add the attribute", + format!("#[non_exhaustive] {snippet}"), + Applicability::Unspecified, + ); } diag.span_help(variant_span, "remove this variant"); }, diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 930386a60aa..a23000e5fe1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -961,7 +961,7 @@ declare_clippy_lint! { /// _ => todo!(), /// } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub REDUNDANT_GUARDS, complexity, "checks for unnecessary guards in match expressions" diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs index 7c0485914b8..8f0083f812c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt}; +use clippy_utils::consts::{constant, constant_full_int, mir_to_const, FullInt}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; @@ -37,14 +37,14 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, None => { let min_val_const = ty.numeric_min_val(cx.tcx)?; - miri_to_const(cx, mir::Const::from_ty_const(min_val_const, cx.tcx))? + mir_to_const(cx, mir::Const::from_ty_const(min_val_const, cx.tcx))? }, }; let rhs_const = match rhs { Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, None => { let max_val_const = ty.numeric_max_val(cx.tcx)?; - miri_to_const(cx, mir::Const::from_ty_const(max_val_const, cx.tcx))? + mir_to_const(cx, mir::Const::from_ty_const(max_val_const, cx.tcx))? }, }; let lhs_val = lhs_const.int_value(cx, ty)?; diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index e7fcef9e9de..a935aea5075 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -2970,7 +2970,7 @@ declare_clippy_lint! { /// assert_eq!((*any_box).type_id(), TypeId::of::<i32>()); /// // ^ dereference first, to call `type_id` on `dyn Any` /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub TYPE_ID_ON_BOX, suspicious, "calling `.type_id()` on `Box<dyn Any>`" @@ -3368,6 +3368,7 @@ declare_clippy_lint! { } declare_clippy_lint! { + /// ### What it does /// Looks for calls to [`Stdin::read_line`] to read a line from the standard input /// into a string, then later attempting to parse this string into a type without first trimming it, which will /// always fail because the string has a trailing newline in it. @@ -3390,7 +3391,7 @@ declare_clippy_lint! { /// // ^^^^^^^^^^^ remove the trailing newline /// assert_eq!(num, 42); /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub READ_LINE_WITHOUT_TRIM, correctness, "calling `Stdin::read_line`, then trying to parse it without first trimming" @@ -3418,7 +3419,7 @@ declare_clippy_lint! { /// # let c = 'c'; /// matches!(c, '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~'); /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub STRING_LIT_CHARS_ANY, restriction, "checks for `<string_lit>.chars().any(|i| i == c)`" @@ -3453,7 +3454,7 @@ declare_clippy_lint! { /// }) /// } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub FORMAT_COLLECT, perf, "`format!`ing every element in a collection, then collecting the strings into a new `String`" @@ -3474,7 +3475,7 @@ declare_clippy_lint! { /// let y = v.iter().collect::<Vec<_>>(); /// assert_eq!(x, y); /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub ITER_SKIP_ZERO, correctness, "disallows `.skip(0)`" @@ -3505,7 +3506,7 @@ declare_clippy_lint! { /// # let v = vec![]; /// _ = v.into_iter().filter(|i| i % 2 == 0).map(|i| really_expensive_fn(i)); /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub FILTER_MAP_BOOL_THEN, style, "checks for usage of `bool::then` in `Iterator::filter_map`" diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index 24c0ea3f60a..3e19d72ec91 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -24,7 +24,6 @@ fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) - } } -#[expect(clippy::too_many_lines)] pub(super) fn check( cx: &LateContext<'_>, expr: &hir::Expr<'_>, @@ -101,11 +100,11 @@ pub(super) fn check( (expr.span.with_lo(args[0].span.hi()), String::new()), ]), ("None", "unwrap_or_else", _) => match args[0].kind { - hir::ExprKind::Closure(hir::Closure { - body, - .. - }) => Some(vec![ - (expr.span.with_hi(cx.tcx.hir().body(*body).value.span.lo()), String::new()), + hir::ExprKind::Closure(hir::Closure { body, .. }) => Some(vec![ + ( + expr.span.with_hi(cx.tcx.hir().body(*body).value.span.lo()), + String::new(), + ), (expr.span.with_lo(args[0].span.hi()), String::new()), ]), _ => None, diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs index c17f00c4275..136947a2c8c 100644 --- a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs +++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs @@ -15,6 +15,10 @@ declare_clippy_lint! { /// A good custom message should be more about why the failure of the assertion is problematic /// and not what is failed because the assertion already conveys that. /// + /// Although the same reasoning applies to testing functions, this lint ignores them as they would be too noisy. + /// Also, in most cases understanding the test failure would be easier + /// compared to understanding a complex invariant distributed around the codebase. + /// /// ### Known problems /// This lint cannot check the quality of the custom panic messages. /// Hence, you can suppress this lint simply by adding placeholder messages diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index ea6a9bc5657..e87aea263d4 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -135,10 +135,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { } fn stmt_might_diverge(stmt: &Stmt<'_>) -> bool { - match stmt.kind { - StmtKind::Item(..) => false, - _ => true, - } + !matches!(stmt.kind, StmtKind::Item(..)) } impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { @@ -148,7 +145,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { ExprKind::Block(block, ..) => match (block.stmts, block.expr) { (stmts, Some(e)) => { if stmts.iter().all(|stmt| !stmt_might_diverge(stmt)) { - self.visit_expr(e) + self.visit_expr(e); } }, ([first @ .., stmt], None) => { diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index 57652e5ff54..212d6234bdb 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -47,9 +47,9 @@ declare_clippy_lint! { /// 12 + *y /// } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub NEEDLESS_PASS_BY_REF_MUT, - suspicious, + nursery, "using a `&mut` argument when it's not mutated" } diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs index 4be140647fc..0e4b6aa1b7d 100644 --- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs +++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs @@ -102,7 +102,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub NON_CANONICAL_PARTIAL_ORD_IMPL, suspicious, "non-canonical implementation of `PartialOrd` on an `Ord` type" diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index 4635e1164cd..6b247cf5f6a 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -312,7 +312,7 @@ declare_clippy_lint! { /// # let status_code = 200; /// if status_code <= 400 && status_code > 500 {} /// ``` - #[clippy::version = "1.71.0"] + #[clippy::version = "1.73.0"] pub IMPOSSIBLE_COMPARISONS, correctness, "double comparisons that will never evaluate to `true`" @@ -332,7 +332,7 @@ declare_clippy_lint! { /// # let status_code = 200; /// if status_code <= 400 && status_code < 500 {} /// ``` - #[clippy::version = "1.71.0"] + #[clippy::version = "1.73.0"] pub REDUNDANT_COMPARISONS, correctness, "double comparisons where one of them can be removed" diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index 8a7e4874666..c951d9a4a09 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs @@ -75,6 +75,7 @@ impl EarlyLintPass for RawStrings { if !snippet(cx, expr.span, prefix).trim().starts_with(prefix) { return; } + let descr = lit.kind.descr(); if !str.contains(['\\', '"']) { span_lint_and_then( @@ -89,20 +90,17 @@ impl EarlyLintPass for RawStrings { let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1); let start = start.with_lo(r_pos); - if end.is_empty() { - diag.span_suggestion( - start, - "use a string literal instead", - format!("\"{str}\""), - Applicability::MachineApplicable, - ); - } else { - diag.multipart_suggestion( - "try", - vec![(start, String::new()), (end, String::new())], - Applicability::MachineApplicable, - ); + let mut remove = vec![(start, String::new())]; + // avoid debug ICE from empty suggestions + if !end.is_empty() { + remove.push((end, String::new())); } + + diag.multipart_suggestion_verbose( + format!("use a plain {descr} literal instead"), + remove, + Applicability::MachineApplicable, + ); }, ); if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) { @@ -149,9 +147,9 @@ impl EarlyLintPass for RawStrings { let (start, end) = hash_spans(expr.span, prefix, req, max); let message = match max - req { - _ if req == 0 => "remove all the hashes around the literal".to_string(), - 1 => "remove one hash from both sides of the literal".to_string(), - n => format!("remove {n} hashes from both sides of the literal"), + _ if req == 0 => format!("remove all the hashes around the {descr} literal"), + 1 => format!("remove one hash from both sides of the {descr} literal"), + n => format!("remove {n} hashes from both sides of the {descr} literal"), }; diag.multipart_suggestion( diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index 0c89c7ee47d..197742b5dd4 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -3,11 +3,9 @@ use clippy_utils::is_from_proc_macro; use clippy_utils::ty::needs_ordered_drop; use rustc_ast::Mutability; use rustc_hir::def::Res; -use rustc_hir::{ - BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath, -}; +use rustc_hir::{BindingAnnotation, ByRef, ExprKind, HirId, Local, Node, Pat, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::{in_external_macro, is_from_async_await}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; use rustc_span::DesugaringKind; @@ -39,7 +37,7 @@ declare_clippy_lint! { /// // no redefinition with the same name /// } /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.73.0"] pub REDUNDANT_LOCALS, correctness, "redundant redefinition of a local binding" @@ -68,21 +66,18 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals { // the local does not change the effect of assignments to the binding. see #11290 if !affects_assignments(cx, mutability, binding_id, local.hir_id); // the local does not affect the code's drop behavior - if !affects_drop_behavior(cx, binding_id, local.hir_id, expr); + if !needs_ordered_drop(cx, cx.typeck_results().expr_ty(expr)); // the local is user-controlled if !in_external_macro(cx.sess(), local.span); if !is_from_proc_macro(cx, expr); - // Async function parameters are lowered into the closure body, so we can't lint them. - // see `lower_maybe_async_body` in `rust_ast_lowering` - if !is_from_async_await(local.span); then { span_lint_and_help( cx, REDUNDANT_LOCALS, - vec![binding_pat.span, local.span], - "redundant redefinition of a binding", - None, - &format!("remove the redefinition of `{ident}`"), + local.span, + &format!("redundant redefinition of a binding `{ident}`"), + Some(binding_pat.span), + &format!("`{ident}` is initially defined here"), ); } } @@ -109,18 +104,3 @@ fn affects_assignments(cx: &LateContext<'_>, mutability: Mutability, bind: HirId // the binding is mutable and the rebinding is in a different scope than the original binding mutability == Mutability::Mut && hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind) } - -/// Check if a rebinding of a local affects the code's drop behavior. -fn affects_drop_behavior<'tcx>( - cx: &LateContext<'tcx>, - bind: HirId, - rebind: HirId, - rebind_expr: &Expr<'tcx>, -) -> bool { - let hir = cx.tcx.hir(); - - // the rebinding is in a different scope than the original binding - // and the type of the binding cares about drop order - hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind) - && needs_ordered_drop(cx, cx.typeck_results().expr_ty(rebind_expr)) -} diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index 5f54a10d1c4..b7396535eed 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -1,9 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, Path, PathSegment}; -use rustc_lint::{LateContext, LateLintPass}; +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::kw; use rustc_span::{sym, Span}; @@ -99,6 +101,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { if let Res::Def(_, def_id) = path.res && let Some(first_segment) = get_first_segment(path) && is_stable(cx, def_id) + && !in_external_macro(cx.sess(), path.span) + && !is_from_proc_macro(cx, &first_segment.ident) { let (lint, used_mod, replace_with) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 75c3c7a958a..23da1de7730 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -294,7 +294,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE. /// /// The minimum rust version that the project supports (msrv: Option<String> = None), @@ -744,3 +744,44 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) { (rows, column_widths) } + +#[cfg(test)] +mod tests { + use rustc_data_structures::fx::{FxHashMap, FxHashSet}; + use serde::de::IgnoredAny; + use std::fs; + use walkdir::WalkDir; + + #[test] + fn configs_are_tested() { + let mut names: FxHashSet<String> = super::metadata::get_configuration_metadata() + .into_iter() + .map(|meta| meta.name.replace('_', "-")) + .collect(); + + let toml_files = WalkDir::new("../tests") + .into_iter() + .map(Result::unwrap) + .filter(|entry| entry.file_name() == "clippy.toml"); + + for entry in toml_files { + let file = fs::read_to_string(entry.path()).unwrap(); + #[allow(clippy::zero_sized_map_values)] + if let Ok(map) = toml::from_str::<FxHashMap<String, IgnoredAny>>(&file) { + for name in map.keys() { + names.remove(name.as_str()); + } + } + } + + assert!( + names.remove("allow-one-hash-in-raw-strings"), + "remove this when #11481 is fixed" + ); + + assert!( + names.is_empty(), + "Configuration variable lacks test: {names:?}\nAdd a test to `tests/ui-toml`" + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index d09d02a7dfd..70b83149ce1 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -132,6 +132,7 @@ impl LateLintPass<'_> for WildcardImports { if self.warn_on_all || !self.check_exceptions(item, use_path.segments); let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id); if !used_imports.is_empty(); // Already handled by `unused_imports` + if !used_imports.contains(&kw::Underscore); then { let mut applicability = Applicability::MachineApplicable; let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index da083fb14aa..855aefa70cb 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -3,12 +3,15 @@ use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_ast::token::LitKind; -use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait}; +use rustc_ast::{ + FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, + FormatTrait, +}; use rustc_errors::Applicability; use rustc_hir::{Expr, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, BytePos}; +use rustc_span::{sym, BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -450,6 +453,12 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos); + let lint_name = if name.starts_with("write") { + WRITE_LITERAL + } else { + PRINT_LITERAL + }; + let mut counts = vec![0u32; format_args.arguments.all_args().len()]; for piece in &format_args.template { if let FormatArgsPiece::Placeholder(placeholder) = piece { @@ -457,6 +466,12 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { } } + let mut suggestion: Vec<(Span, String)> = vec![]; + // holds index of replaced positional arguments; used to decrement the index of the remaining + // positional arguments. + let mut replaced_position: Vec<usize> = vec![]; + let mut sug_span: Option<Span> = None; + for piece in &format_args.template { if let FormatArgsPiece::Placeholder(FormatPlaceholder { argument, @@ -471,9 +486,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind && !arg.expr.span.from_expansion() && let Some(value_string) = snippet_opt(cx, arg.expr.span) - { + { let (replacement, replace_raw) = match lit.kind { - LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) { + LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) { Some(extracted) => extracted, None => return, }, @@ -493,12 +508,6 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { _ => continue, }; - let lint = if name.starts_with("write") { - WRITE_LITERAL - } else { - PRINT_LITERAL - }; - let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { continue }; let format_string_is_raw = format_string_snippet.starts_with('r'); @@ -519,29 +528,58 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { }, }; - span_lint_and_then( - cx, - lint, - arg.expr.span, - "literal with an empty format string", - |diag| { - if let Some(replacement) = replacement - // `format!("{}", "a")`, `format!("{named}", named = "b") - // ~~~~~ ~~~~~~~~~~~~~ - && let Some(removal_span) = format_arg_removal_span(format_args, index) - { - let replacement = replacement.replace('{', "{{").replace('}', "}}"); - diag.multipart_suggestion( - "try", - vec![(*placeholder_span, replacement), (removal_span, String::new())], - Applicability::MachineApplicable, - ); - } - }, - ); + sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span)); + + if let Some((_, index)) = positional_arg_piece_span(piece) { + replaced_position.push(index); + } + + if let Some(replacement) = replacement + // `format!("{}", "a")`, `format!("{named}", named = "b") + // ~~~~~ ~~~~~~~~~~~~~ + && let Some(removal_span) = format_arg_removal_span(format_args, index) { + let replacement = escape_braces(&replacement, !format_string_is_raw && !replace_raw); + suggestion.push((*placeholder_span, replacement)); + suggestion.push((removal_span, String::new())); + } + } + } + // Decrement the index of the remaining by the number of replaced positional arguments + if !suggestion.is_empty() { + for piece in &format_args.template { + if let Some((span, index)) = positional_arg_piece_span(piece) + && suggestion.iter().all(|(s, _)| *s != span) { + let decrement = replaced_position.iter().filter(|i| **i < index).count(); + suggestion.push((span, format!("{{{}}}", index.saturating_sub(decrement)))); + } } } + + if let Some(span) = sug_span { + span_lint_and_then(cx, lint_name, span, "literal with an empty format string", |diag| { + if !suggestion.is_empty() { + diag.multipart_suggestion("try", suggestion, Applicability::MachineApplicable); + } + }); + } +} + +/// Extract Span and its index from the given `piece`, iff it's positional argument. +fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> { + match piece { + FormatArgsPiece::Placeholder(FormatPlaceholder { + argument: + FormatArgPosition { + index: Ok(index), + kind: FormatArgPositionKind::Number, + .. + }, + span: Some(span), + .. + }) => Some((*span, *index)), + _ => None, + } } /// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw @@ -593,3 +631,47 @@ fn conservative_unescape(literal: &str) -> Result<String, UnescapeErr> { if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) } } + +/// Replaces `{` with `{{` and `}` with `}}`. If `preserve_unicode_escapes` is `true` the braces in +/// `\u{xxxx}` are left unmodified +#[expect(clippy::match_same_arms)] +fn escape_braces(literal: &str, preserve_unicode_escapes: bool) -> String { + #[derive(Clone, Copy)] + enum State { + Normal, + Backslash, + UnicodeEscape, + } + + let mut escaped = String::with_capacity(literal.len()); + let mut state = State::Normal; + + for ch in literal.chars() { + state = match (ch, state) { + // Escape braces outside of unicode escapes by doubling them up + ('{' | '}', State::Normal) => { + escaped.push(ch); + State::Normal + }, + // If `preserve_unicode_escapes` isn't enabled stay in `State::Normal`, otherwise: + // + // \u{aaaa} \\ \x01 + // ^ ^ ^ + ('\\', State::Normal) if preserve_unicode_escapes => State::Backslash, + // \u{aaaa} + // ^ + ('u', State::Backslash) => State::UnicodeEscape, + // \xAA \\ + // ^ ^ + (_, State::Backslash) => State::Normal, + // \u{aaaa} + // ^ + ('}', State::UnicodeEscape) => State::Normal, + _ => state, + }; + + escaped.push(ch); + } + + escaped +} diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 1596bb77397..8522493f67b 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.74" +version = "0.1.75" edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index d596eed4b7c..0bae7056c4f 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -9,11 +9,12 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; use rustc_lexer::tokenize; use rustc_lint::LateContext; -use rustc_middle::mir::interpret::Scalar; +use rustc_middle::mir::interpret::{alloc_range, Scalar}; use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, List, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::SyntaxContext; +use rustc_target::abi::Size; use std::cmp::Ordering::{self, Equal}; use std::hash::{Hash, Hasher}; use std::iter; @@ -403,7 +404,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { && adt_def.is_struct() && let Some(desired_field) = field_of_struct(*adt_def, self.lcx, *constant, field) { - miri_to_const(self.lcx, desired_field) + mir_to_const(self.lcx, desired_field) } else { result @@ -483,7 +484,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), None) .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty))?; - let result = miri_to_const(self.lcx, result)?; + let result = mir_to_const(self.lcx, result)?; self.source = ConstantSource::Constant; Some(result) }, @@ -655,10 +656,14 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } -pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> { +pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> { use rustc_middle::mir::ConstValue; - match result { - mir::Const::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { + let mir::Const::Val(val, _) = result else { + // We only work on evaluated consts. + return None; + }; + match (val, result.ty().kind()) { + (ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))), @@ -671,42 +676,28 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))), _ => None, }, - mir::Const::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => - { - let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; + (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => { + let data = val.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; String::from_utf8(data.to_owned()).ok().map(Constant::Str) }, - mir::Const::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => { - let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory(); - match result.ty().kind() { - ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), - ty::Array(sub_type, len) => match sub_type.kind() { - ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) { - Some(len) => alloc - .inner() - .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap())) - .to_owned() - .array_chunks::<4>() - .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk)))) - .collect::<Option<Vec<Constant<'tcx>>>>() - .map(Constant::Vec), - _ => None, - }, - ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) { - Some(len) => alloc - .inner() - .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap())) - .to_owned() - .array_chunks::<8>() - .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk)))) - .collect::<Option<Vec<Constant<'tcx>>>>() - .map(Constant::Vec), - _ => None, - }, - _ => None, - }, - _ => None, + (_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)), + (ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => { + let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); + let len = len.try_to_target_usize(lcx.tcx)?; + let ty::Float(flt) = sub_type.kind() else { + return None; + }; + let size = Size::from_bits(flt.bit_width()); + let mut res = Vec::new(); + for idx in 0..len { + let range = alloc_range(offset + size * idx, size); + let val = alloc.read_scalar(&lcx.tcx, range, /* read_provenance */ false).ok()?; + res.push(match flt { + FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)), + FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().ok()?)), + }); } + Some(Constant::Vec(res)) }, _ => None, } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 64ca2ff7873..c2c97259d38 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -80,7 +80,6 @@ use std::sync::{Mutex, MutexGuard, OnceLock}; use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{self, LitKind, RangeLimits}; -use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_hir::def::{DefKind, Res}; @@ -113,7 +112,7 @@ use rustc_span::{sym, Span}; use rustc_target::abi::Integer; use visitors::Visitable; -use crate::consts::{constant, miri_to_const, Constant}; +use crate::consts::{constant, mir_to_const, Constant}; use crate::higher::Range; use crate::ty::{ adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, @@ -1509,9 +1508,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx) - && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree())) - && let min_const_kind = Const::from_value(const_val, bnd_ty) - && let Some(min_const) = miri_to_const(cx, min_const_kind) + && let Some(min_const) = mir_to_const(cx, Const::from_ty_const(min_val, cx.tcx)) && let Some(start_const) = constant(cx, cx.typeck_results(), start) { start_const == min_const @@ -1525,9 +1522,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx) - && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree())) - && let max_const_kind = Const::from_value(const_val, bnd_ty) - && let Some(max_const) = miri_to_const(cx, max_const_kind) + && let Some(max_const) = mir_to_const(cx, Const::from_ty_const(max_val, cx.tcx)) && let Some(end_const) = constant(cx, cx.typeck_results(), end) { end_const == max_const @@ -2456,11 +2451,12 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { }) } -/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied +/// Checks if `id` has a `#[cfg(test)]` attribute applied /// -/// Note: Add `//@compile-flags: --test` to UI tests with a `#[cfg(test)]` function -pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { - fn is_cfg_test(attr: &Attribute) -> bool { +/// This only checks directly applied attributes, to see if a node is inside a `#[cfg(test)]` parent +/// use [`is_in_cfg_test`] +pub fn is_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { + tcx.hir().attrs(id).iter().any(|attr| { if attr.has_name(sym::cfg) && let Some(items) = attr.meta_item_list() && let [item] = &*items @@ -2470,11 +2466,14 @@ pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { } else { false } - } + }) +} + +/// Checks if any parent node of `HirId` has `#[cfg(test)]` attribute applied +pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { tcx.hir() - .parent_iter(id) - .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id)) - .any(is_cfg_test) + .parent_id_iter(id) + .any(|parent_id| is_cfg_test(tcx, parent_id)) } /// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied. diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 0faff05eb23..df839c2106f 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -19,7 +19,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,71,0 { TUPLE_ARRAY_CONVERSIONS } + 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,70,0 { OPTION_IS_SOME_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 3b47a451345..d752fe7d97e 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -62,6 +62,27 @@ where } } } +impl<'tcx, A, B> Visitable<'tcx> for (A, B) +where + A: Visitable<'tcx>, + B: Visitable<'tcx>, +{ + fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) { + let (a, b) = self; + a.visit(visitor); + b.visit(visitor); + } +} +impl<'tcx, T> Visitable<'tcx> for Option<T> +where + T: Visitable<'tcx>, +{ + fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) { + if let Some(x) = self { + x.visit(visitor); + } + } +} macro_rules! visitable_ref { ($t:ident, $f:ident) => { impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { @@ -748,3 +769,26 @@ pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool { }) .is_some() } + +/// If the local is only used once in `visitable` returns the path expression referencing the given +/// local +pub fn local_used_once<'tcx>( + cx: &LateContext<'tcx>, + visitable: impl Visitable<'tcx>, + id: HirId, +) -> Option<&'tcx Expr<'tcx>> { + let mut expr = None; + + let cf = for_each_expr_with_closures(cx, visitable, |e| { + if path_to_local_id(e, id) && expr.replace(e).is_some() { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }); + if cf.is_some() { + return None; + } + + expr +} diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml index 1470da61fac..beea9fd00e7 100644 --- a/src/tools/clippy/declare_clippy_lint/Cargo.toml +++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.74" +version = "0.1.75" edition = "2021" publish = false diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 5ce22b65f00..fe2c77ab47f 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-09-25" +channel = "nightly-2023-10-06" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 1d89477dcc1..d47767faed9 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -26,6 +26,8 @@ use std::ops::Deref; use std::path::Path; use std::process::exit; +use anstream::println; + /// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If /// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`. fn arg_value<'a, T: Deref<Target = str>>( @@ -162,39 +164,15 @@ impl rustc_driver::Callbacks for ClippyCallbacks { } } +#[allow(clippy::ignored_unit_patterns)] fn display_help() { - println!( - "\ -Checks a package to catch common mistakes and improve your Rust code. - -Usage: - cargo clippy [options] [--] [<opts>...] - -Common options: - -h, --help Print this message - --rustc Pass all args to rustc - -V, --version Print version info and exit - -For the other options see `cargo check --help`. - -To allow or deny a lint from the command line you can use `cargo clippy --` -with: - - -W --warn OPT Set lint warnings - -A --allow OPT Set lint allowed - -D --deny OPT Set lint denied - -F --forbid OPT Set lint forbidden - -You can use tool lints to allow or deny lints from your code, eg.: - - #[allow(clippy::needless_lifetimes)] -" - ); + println!("{}", help_message()); } const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml"; #[allow(clippy::too_many_lines)] +#[allow(clippy::ignored_unit_patterns)] pub fn main() { let handler = EarlyErrorHandler::new(ErrorOutputType::default()); @@ -236,6 +214,7 @@ pub fn main() { if orig_args.iter().any(|a| a == "--version" || a == "-V") { let version_info = rustc_tools_util::get_version_info!(); + println!("{version_info}"); exit(0); } @@ -292,3 +271,25 @@ pub fn main() { } })) } + +#[must_use] +fn help_message() -> &'static str { + color_print::cstr!( + "Checks a file to catch common mistakes and improve your Rust code. +Run <cyan>clippy-driver</> with the same arguments you use for <cyan>rustc</> + +<green,bold>Usage</>: + <cyan,bold>clippy-driver</> <cyan>[OPTIONS] INPUT</> + +<green,bold>Common options:</> + <cyan,bold>-h</>, <cyan,bold>--help</> Print this message + <cyan,bold>-V</>, <cyan,bold>--version</> Print version info and exit + <cyan,bold>--rustc</> Pass all arguments to <cyan>rustc</> + +<green,bold>Allowing / Denying lints</> +You can use tool lints to allow or deny lints from your code, e.g.: + + <yellow,bold>#[allow(clippy::needless_lifetimes)]</> +" + ) +} diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs index 26b655076cf..bbf7d22c850 100644 --- a/src/tools/clippy/src/main.rs +++ b/src/tools/clippy/src/main.rs @@ -6,37 +6,14 @@ use std::env; use std::path::PathBuf; use std::process::{self, Command}; -const CARGO_CLIPPY_HELP: &str = "Checks a package to catch common mistakes and improve your Rust code. - -Usage: - cargo clippy [options] [--] [<opts>...] - -Common options: - --no-deps Run Clippy only on the given crate, without linting the dependencies - --fix Automatically apply lint suggestions. This flag implies `--no-deps` and `--all-targets` - -h, --help Print this message - -V, --version Print version info and exit - --explain LINT Print the documentation for a given lint - -For the other options see `cargo check --help`. - -To allow or deny a lint from the command line you can use `cargo clippy --` -with: - - -W --warn OPT Set lint warnings - -A --allow OPT Set lint allowed - -D --deny OPT Set lint denied - -F --forbid OPT Set lint forbidden - -You can use tool lints to allow or deny lints from your code, e.g.: - - #[allow(clippy::needless_lifetimes)] -"; +use anstream::println; +#[allow(clippy::ignored_unit_patterns)] fn show_help() { - println!("{CARGO_CLIPPY_HELP}"); + println!("{}", help_message()); } +#[allow(clippy::ignored_unit_patterns)] fn show_version() { let version_info = rustc_tools_util::get_version_info!(); println!("{version_info}"); @@ -168,6 +145,38 @@ where } } +#[must_use] +pub fn help_message() -> &'static str { + color_print::cstr!( +"Checks a package to catch common mistakes and improve your Rust code. + +<green,bold>Usage</>: + <cyan,bold>cargo clippy</> <cyan>[OPTIONS] [--] [<<ARGS>>...]</> + +<green,bold>Common options:</> + <cyan,bold>--no-deps</> Run Clippy only on the given crate, without linting the dependencies + <cyan,bold>--fix</> Automatically apply lint suggestions. This flag implies <cyan>--no-deps</> and <cyan>--all-targets</> + <cyan,bold>-h</>, <cyan,bold>--help</> Print this message + <cyan,bold>-V</>, <cyan,bold>--version</> Print version info and exit + <cyan,bold>--explain [LINT]</> Print the documentation for a given lint + +See all options with <cyan,bold>cargo check --help</>. + +<green,bold>Allowing / Denying lints</> + +To allow or deny a lint from the command line you can use <cyan,bold>cargo clippy --</> with: + + <cyan,bold>-W</> / <cyan,bold>--warn</> <cyan>[LINT]</> Set lint warnings + <cyan,bold>-A</> / <cyan,bold>--allow</> <cyan>[LINT]</> Set lint allowed + <cyan,bold>-D</> / <cyan,bold>--deny</> <cyan>[LINT]</> Set lint denied + <cyan,bold>-F</> / <cyan,bold>--forbid</> <cyan>[LINT]</> Set lint forbidden + +You can use tool lints to allow or deny lints from your code, e.g.: + + <yellow,bold>#[allow(clippy::needless_lifetimes)]</> +" + ) +} #[cfg(test)] mod tests { use super::ClippyCmd; diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs index 37f0ec2b37d..79a95d775b1 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -21,6 +21,18 @@ pub fn derive(_: TokenStream) -> TokenStream { output } +#[proc_macro_derive(ImplStructWithStdDisplay)] +pub fn derive_std(_: TokenStream) -> TokenStream { + quote! { + struct A {} + impl ::std::fmt::Display for A { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "A") + } + } + } +} + #[proc_macro_derive(FieldReassignWithDefault)] pub fn derive_foo(_input: TokenStream) -> TokenStream { quote! { @@ -141,3 +153,19 @@ pub fn shadow_derive(_: TokenStream) -> TokenStream { .into(), ]) } + +#[proc_macro_derive(StructIgnoredUnitPattern)] +pub fn derive_ignored_unit_pattern(_: TokenStream) -> TokenStream { + quote! { + struct A; + impl A { + fn a(&self) -> Result<(), ()> { + unimplemented!() + } + + pub fn b(&self) { + let _ = self.a().unwrap(); + } + } + } +} diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed index 6c6f21fee16..15eaf1f659a 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed @@ -1,3 +1,4 @@ +//@aux-build:proc_macro_derive.rs #![warn(clippy::ignored_unit_patterns)] #![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)] @@ -14,8 +15,22 @@ fn main() { //~^ ERROR: matching over `()` is more explicit let _ = foo().map_err(|()| todo!()); //~^ ERROR: matching over `()` is more explicit + + println!( + "{:?}", + match foo() { + Ok(()) => {}, + //~^ ERROR: matching over `()` is more explicit + Err(()) => {}, + //~^ ERROR: matching over `()` is more explicit + } + ); } +// ignored_unit_patterns in derive macro should be ok +#[derive(proc_macro_derive::StructIgnoredUnitPattern)] +pub struct B; + #[allow(unused)] pub fn moo(_: ()) { let () = foo().unwrap(); diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs index 5e8c2e03ba2..9cac3a440ab 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs @@ -1,3 +1,4 @@ +//@aux-build:proc_macro_derive.rs #![warn(clippy::ignored_unit_patterns)] #![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)] @@ -14,8 +15,22 @@ fn main() { //~^ ERROR: matching over `()` is more explicit let _ = foo().map_err(|_| todo!()); //~^ ERROR: matching over `()` is more explicit + + println!( + "{:?}", + match foo() { + Ok(_) => {}, + //~^ ERROR: matching over `()` is more explicit + Err(_) => {}, + //~^ ERROR: matching over `()` is more explicit + } + ); } +// ignored_unit_patterns in derive macro should be ok +#[derive(proc_macro_derive::StructIgnoredUnitPattern)] +pub struct B; + #[allow(unused)] pub fn moo(_: ()) { let _ = foo().unwrap(); diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr index df5e1d89e90..cac01a87dba 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr @@ -1,5 +1,5 @@ error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:10:12 + --> $DIR/ignored_unit_patterns.rs:11:12 | LL | Ok(_) => {}, | ^ help: use `()` instead of `_`: `()` @@ -8,28 +8,40 @@ LL | Ok(_) => {}, = help: to override `-D warnings` add `#[allow(clippy::ignored_unit_patterns)]` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:11:13 + --> $DIR/ignored_unit_patterns.rs:12:13 | LL | Err(_) => {}, | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:13:15 + --> $DIR/ignored_unit_patterns.rs:14:15 | LL | if let Ok(_) = foo() {} | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:15:28 + --> $DIR/ignored_unit_patterns.rs:16:28 | LL | let _ = foo().map_err(|_| todo!()); | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:21:9 + --> $DIR/ignored_unit_patterns.rs:22:16 + | +LL | Ok(_) => {}, + | ^ help: use `()` instead of `_`: `()` + +error: matching over `()` is more explicit + --> $DIR/ignored_unit_patterns.rs:24:17 + | +LL | Err(_) => {}, + | ^ help: use `()` instead of `_`: `()` + +error: matching over `()` is more explicit + --> $DIR/ignored_unit_patterns.rs:36:9 | LL | let _ = foo().unwrap(); | ^ help: use `()` instead of `_`: `()` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/infinite_loop.rs b/src/tools/clippy/tests/ui/infinite_loop.rs index 281e12c7b93..765c6701147 100644 --- a/src/tools/clippy/tests/ui/infinite_loop.rs +++ b/src/tools/clippy/tests/ui/infinite_loop.rs @@ -7,8 +7,6 @@ fn fn_constref(i: &i32) -> i32 { unimplemented!() } fn fn_mutref(i: &mut i32) { - //~^ ERROR: this argument is a mutable reference, but not used mutably - //~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` unimplemented!() } fn fooi() -> i32 { diff --git a/src/tools/clippy/tests/ui/infinite_loop.stderr b/src/tools/clippy/tests/ui/infinite_loop.stderr index c32b5e323c0..a78e47d0229 100644 --- a/src/tools/clippy/tests/ui/infinite_loop.stderr +++ b/src/tools/clippy/tests/ui/infinite_loop.stderr @@ -1,5 +1,5 @@ error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:24:11 + --> $DIR/infinite_loop.rs:22:11 | LL | while y < 10 { | ^^^^^^ @@ -8,7 +8,7 @@ LL | while y < 10 { = note: `#[deny(clippy::while_immutable_condition)]` on by default error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:31:11 + --> $DIR/infinite_loop.rs:29:11 | LL | while y < 10 && x < 3 { | ^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | while y < 10 && x < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:40:11 + --> $DIR/infinite_loop.rs:38:11 | LL | while !cond { | ^^^^^ @@ -24,7 +24,7 @@ LL | while !cond { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:86:11 + --> $DIR/infinite_loop.rs:84:11 | LL | while i < 3 { | ^^^^^ @@ -32,7 +32,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:93:11 + --> $DIR/infinite_loop.rs:91:11 | LL | while i < 3 && j > 0 { | ^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | while i < 3 && j > 0 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:99:11 + --> $DIR/infinite_loop.rs:97:11 | LL | while i < 3 { | ^^^^^ @@ -48,7 +48,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:116:11 + --> $DIR/infinite_loop.rs:114:11 | LL | while i < 3 { | ^^^^^ @@ -56,7 +56,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:123:11 + --> $DIR/infinite_loop.rs:121:11 | LL | while i < 3 { | ^^^^^ @@ -64,7 +64,7 @@ LL | while i < 3 { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:191:15 + --> $DIR/infinite_loop.rs:189:15 | LL | while self.count < n { | ^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | while self.count < n { = note: this may lead to an infinite or to a never running loop error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:201:11 + --> $DIR/infinite_loop.rs:199:11 | LL | while y < 10 { | ^^^^^^ @@ -82,7 +82,7 @@ LL | while y < 10 { = help: rewrite it as `if cond { loop { } }` error: variables in the condition are not mutated in the loop body - --> $DIR/infinite_loop.rs:210:11 + --> $DIR/infinite_loop.rs:208:11 | LL | while y < 10 { | ^^^^^^ @@ -91,14 +91,5 @@ LL | while y < 10 { = note: this loop contains `return`s or `break`s = help: rewrite it as `if cond { loop { } }` -error: this argument is a mutable reference, but not used mutably - --> $DIR/infinite_loop.rs:9:17 - | -LL | fn fn_mutref(i: &mut i32) { - | ^^^^^^^^ help: consider changing to: `&i32` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` - -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/into_iter_without_iter.rs b/src/tools/clippy/tests/ui/into_iter_without_iter.rs new file mode 100644 index 00000000000..6be3bb8abdd --- /dev/null +++ b/src/tools/clippy/tests/ui/into_iter_without_iter.rs @@ -0,0 +1,124 @@ +//@no-rustfix +#![warn(clippy::into_iter_without_iter)] + +use std::iter::IntoIterator; + +fn main() { + { + struct S; + + impl<'a> IntoIterator for &'a S { + //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter` method + type IntoIter = std::slice::Iter<'a, u8>; + type Item = &'a u8; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + impl<'a> IntoIterator for &'a mut S { + //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter_mut` method + type IntoIter = std::slice::IterMut<'a, u8>; + type Item = &'a mut u8; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } + { + struct S<T>(T); + impl<'a, T> IntoIterator for &'a S<T> { + //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter` method + type IntoIter = std::slice::Iter<'a, T>; + type Item = &'a T; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + impl<'a, T> IntoIterator for &'a mut S<T> { + //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter_mut` method + type IntoIter = std::slice::IterMut<'a, T>; + type Item = &'a mut T; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } + { + // Both iter and iter_mut methods exist, don't lint + struct S<'a, T>(&'a T); + + impl<'a, T> S<'a, T> { + fn iter(&self) -> std::slice::Iter<'a, T> { + todo!() + } + fn iter_mut(&mut self) -> std::slice::IterMut<'a, T> { + todo!() + } + } + + impl<'a, T> IntoIterator for &S<'a, T> { + type IntoIter = std::slice::Iter<'a, T>; + type Item = &'a T; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + + impl<'a, T> IntoIterator for &mut S<'a, T> { + type IntoIter = std::slice::IterMut<'a, T>; + type Item = &'a mut T; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } + { + // Only `iter` exists, no `iter_mut` + struct S<'a, T>(&'a T); + + impl<'a, T> S<'a, T> { + fn iter(&self) -> std::slice::Iter<'a, T> { + todo!() + } + } + + impl<'a, T> IntoIterator for &S<'a, T> { + type IntoIter = std::slice::Iter<'a, T>; + type Item = &'a T; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + + impl<'a, T> IntoIterator for &mut S<'a, T> { + //~^ ERROR: `IntoIterator` implemented for a reference type without an `iter_mut` method + type IntoIter = std::slice::IterMut<'a, T>; + type Item = &'a mut T; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } + { + // `iter` exists, but `IntoIterator` is implemented for an alias. inherent_impls doesn't "normalize" + // aliases so that `inherent_impls(Alias)` where `type Alias = S` returns nothing, so this can lead + // to fun FPs. Make sure it doesn't happen here (we're using type_of, which should skip the alias). + struct S; + + impl S { + fn iter(&self) -> std::slice::Iter<'static, u8> { + todo!() + } + } + + type Alias = S; + + impl IntoIterator for &Alias { + type IntoIter = std::slice::Iter<'static, u8>; + type Item = &'static u8; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } +} diff --git a/src/tools/clippy/tests/ui/into_iter_without_iter.stderr b/src/tools/clippy/tests/ui/into_iter_without_iter.stderr new file mode 100644 index 00000000000..f543d1d8e86 --- /dev/null +++ b/src/tools/clippy/tests/ui/into_iter_without_iter.stderr @@ -0,0 +1,114 @@ +error: `IntoIterator` implemented for a reference type without an `iter` method + --> $DIR/into_iter_without_iter.rs:10:9 + | +LL | / impl<'a> IntoIterator for &'a S { +LL | | +LL | | type IntoIter = std::slice::Iter<'a, u8>; +LL | | type Item = &'a u8; +... | +LL | | } +LL | | } + | |_________^ + | + = note: `-D clippy::into-iter-without-iter` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::into_iter_without_iter)]` +help: consider implementing `iter` + | +LL ~ +LL + impl S { +LL + fn iter(&self) -> std::slice::Iter<'a, u8> { +LL + <&Self as IntoIterator>::into_iter(self) +LL + } +LL + } + | + +error: `IntoIterator` implemented for a reference type without an `iter_mut` method + --> $DIR/into_iter_without_iter.rs:18:9 + | +LL | / impl<'a> IntoIterator for &'a mut S { +LL | | +LL | | type IntoIter = std::slice::IterMut<'a, u8>; +LL | | type Item = &'a mut u8; +... | +LL | | } +LL | | } + | |_________^ + | +help: consider implementing `iter_mut` + | +LL ~ +LL + impl S { +LL + fn iter_mut(&mut self) -> std::slice::IterMut<'a, u8> { +LL + <&mut Self as IntoIterator>::into_iter(self) +LL + } +LL + } + | + +error: `IntoIterator` implemented for a reference type without an `iter` method + --> $DIR/into_iter_without_iter.rs:29:9 + | +LL | / impl<'a, T> IntoIterator for &'a S<T> { +LL | | +LL | | type IntoIter = std::slice::Iter<'a, T>; +LL | | type Item = &'a T; +... | +LL | | } +LL | | } + | |_________^ + | +help: consider implementing `iter` + | +LL ~ +LL + impl S<T> { +LL + fn iter(&self) -> std::slice::Iter<'a, T> { +LL + <&Self as IntoIterator>::into_iter(self) +LL + } +LL + } + | + +error: `IntoIterator` implemented for a reference type without an `iter_mut` method + --> $DIR/into_iter_without_iter.rs:37:9 + | +LL | / impl<'a, T> IntoIterator for &'a mut S<T> { +LL | | +LL | | type IntoIter = std::slice::IterMut<'a, T>; +LL | | type Item = &'a mut T; +... | +LL | | } +LL | | } + | |_________^ + | +help: consider implementing `iter_mut` + | +LL ~ +LL + impl S<T> { +LL + fn iter_mut(&mut self) -> std::slice::IterMut<'a, T> { +LL + <&mut Self as IntoIterator>::into_iter(self) +LL + } +LL + } + | + +error: `IntoIterator` implemented for a reference type without an `iter_mut` method + --> $DIR/into_iter_without_iter.rs:93:9 + | +LL | / impl<'a, T> IntoIterator for &mut S<'a, T> { +LL | | +LL | | type IntoIter = std::slice::IterMut<'a, T>; +LL | | type Item = &'a mut T; +... | +LL | | } +LL | | } + | |_________^ + | +help: consider implementing `iter_mut` + | +LL ~ +LL + impl S<'a, T> { +LL + fn iter_mut(&mut self) -> std::slice::IterMut<'a, T> { +LL + <&mut Self as IntoIterator>::into_iter(self) +LL + } +LL + } + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/items_after_test_module/after_proc_macros.rs b/src/tools/clippy/tests/ui/items_after_test_module/after_proc_macros.rs new file mode 100644 index 00000000000..d9c0aef88c8 --- /dev/null +++ b/src/tools/clippy/tests/ui/items_after_test_module/after_proc_macros.rs @@ -0,0 +1,11 @@ +//@aux-build:../auxiliary/proc_macros.rs +extern crate proc_macros; + +proc_macros::with_span! { + span + #[cfg(test)] + mod tests {} +} + +#[test] +fn f() {} diff --git a/src/tools/clippy/tests/ui/items_after_test_module/auxiliary/submodule.rs b/src/tools/clippy/tests/ui/items_after_test_module/auxiliary/submodule.rs new file mode 100644 index 00000000000..69d61790121 --- /dev/null +++ b/src/tools/clippy/tests/ui/items_after_test_module/auxiliary/submodule.rs @@ -0,0 +1,4 @@ +#[cfg(test)] +mod tests {} + +fn in_submodule() {} diff --git a/src/tools/clippy/tests/ui/items_after_test_module/block_module.stderr b/src/tools/clippy/tests/ui/items_after_test_module/block_module.stderr deleted file mode 100644 index 1b625747161..00000000000 --- a/src/tools/clippy/tests/ui/items_after_test_module/block_module.stderr +++ /dev/null @@ -1,2 +0,0 @@ -error: Option 'test' given more than once - diff --git a/src/tools/clippy/tests/ui/items_after_test_module/in_submodule.rs b/src/tools/clippy/tests/ui/items_after_test_module/in_submodule.rs new file mode 100644 index 00000000000..7132e71764e --- /dev/null +++ b/src/tools/clippy/tests/ui/items_after_test_module/in_submodule.rs @@ -0,0 +1,8 @@ +#[path = "auxiliary/submodule.rs"] +mod submodule; + +#[cfg(test)] +mod tests { + #[test] + fn t() {} +} diff --git a/src/tools/clippy/tests/ui/items_after_test_module/in_submodule.stderr b/src/tools/clippy/tests/ui/items_after_test_module/in_submodule.stderr new file mode 100644 index 00000000000..4e99876365c --- /dev/null +++ b/src/tools/clippy/tests/ui/items_after_test_module/in_submodule.stderr @@ -0,0 +1,14 @@ +error: items after a test module + --> $DIR/auxiliary/submodule.rs:2:1 + | +LL | mod tests {} + | ^^^^^^^^^ +LL | +LL | fn in_submodule() {} + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::items-after-test-module` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::items_after_test_module)]` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/items_after_test_module/multiple_modules.rs b/src/tools/clippy/tests/ui/items_after_test_module/multiple_modules.rs new file mode 100644 index 00000000000..8ab9e8200f1 --- /dev/null +++ b/src/tools/clippy/tests/ui/items_after_test_module/multiple_modules.rs @@ -0,0 +1,11 @@ +#[cfg(test)] +mod tests { + #[test] + fn f() {} +} + +#[cfg(test)] +mod more_tests { + #[test] + fn g() {} +} diff --git a/src/tools/clippy/tests/ui/items_after_test_module/block_module.rs b/src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed index 5136b2557ec..d444100a76b 100644 --- a/src/tools/clippy/tests/ui/items_after_test_module/block_module.rs +++ b/src/tools/clippy/tests/ui/items_after_test_module/root_module.fixed @@ -1,4 +1,3 @@ -//@compile-flags: --test #![allow(unused)] #![warn(clippy::items_after_test_module)] @@ -6,6 +5,13 @@ fn main() {} fn should_not_lint() {} +fn should_lint() {} + +const SHOULD_ALSO_LINT: usize = 1; +macro_rules! should_lint { + () => {}; +} + #[allow(dead_code)] #[allow(unused)] // Some attributes to check that span replacement is good enough #[allow(clippy::allow_attributes)] @@ -14,10 +20,3 @@ mod tests { #[test] fn hi() {} } - -fn should_lint() {} - -const SHOULD_ALSO_LINT: usize = 1; -macro_rules! should_not_lint { - () => {}; -} diff --git a/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs b/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs new file mode 100644 index 00000000000..57da01639cc --- /dev/null +++ b/src/tools/clippy/tests/ui/items_after_test_module/root_module.rs @@ -0,0 +1,22 @@ +#![allow(unused)] +#![warn(clippy::items_after_test_module)] + +fn main() {} + +fn should_not_lint() {} + +#[allow(dead_code)] +#[allow(unused)] // Some attributes to check that span replacement is good enough +#[allow(clippy::allow_attributes)] +#[cfg(test)] +mod tests { + #[test] + fn hi() {} +} + +fn should_lint() {} + +const SHOULD_ALSO_LINT: usize = 1; +macro_rules! should_lint { + () => {}; +} diff --git a/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr b/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr new file mode 100644 index 00000000000..67bc82ebff9 --- /dev/null +++ b/src/tools/clippy/tests/ui/items_after_test_module/root_module.stderr @@ -0,0 +1,20 @@ +error: items after a test module + --> $DIR/root_module.rs:12:1 + | +LL | mod tests { + | ^^^^^^^^^ +... +LL | fn should_lint() {} + | ^^^^^^^^^^^^^^^^ +LL | +LL | const SHOULD_ALSO_LINT: usize = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | macro_rules! should_lint { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::items-after-test-module` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::items_after_test_module)]` + = help: move the items to before the test module was defined + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/iter_without_into_iter.rs b/src/tools/clippy/tests/ui/iter_without_into_iter.rs new file mode 100644 index 00000000000..cedb756c79d --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_without_into_iter.rs @@ -0,0 +1,120 @@ +//@no-rustfix +#![warn(clippy::iter_without_into_iter)] + +fn main() { + { + struct S; + impl S { + pub fn iter(&self) -> std::slice::Iter<'_, u8> { + //~^ ERROR: `iter` method without an `IntoIterator` impl + [].iter() + } + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> { + //~^ ERROR: `iter_mut` method without an `IntoIterator` impl + [].iter_mut() + } + } + } + { + struct S; + impl S { + pub fn iter(&self) -> impl Iterator<Item = &u8> { + // RPITIT is not stable, so we can't generally suggest it here yet + [].iter() + } + } + } + { + struct S<'a>(&'a mut [u8]); + impl<'a> S<'a> { + pub fn iter(&self) -> std::slice::Iter<'_, u8> { + //~^ ERROR: `iter` method without an `IntoIterator` impl + self.0.iter() + } + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> { + //~^ ERROR: `iter_mut` method without an `IntoIterator` impl + self.0.iter_mut() + } + } + } + { + // Incompatible signatures + struct S; + impl S { + pub fn iter(self) -> std::slice::Iter<'static, u8> { + todo!() + } + } + struct S2; + impl S2 { + pub async fn iter(&self) -> std::slice::Iter<'static, u8> { + todo!() + } + } + struct S3; + impl S3 { + pub fn iter(&self, _additional_param: ()) -> std::slice::Iter<'static, u8> { + todo!() + } + } + struct S4<T>(T); + impl<T> S4<T> { + pub fn iter<U>(&self) -> std::slice::Iter<'static, (T, U)> { + todo!() + } + } + struct S5<T>(T); + impl<T> S5<T> { + pub fn iter(&self) -> std::slice::Iter<'static, T> { + todo!() + } + } + } + { + struct S<T>(T); + impl<T> S<T> { + pub fn iter(&self) -> std::slice::Iter<'_, T> { + //~^ ERROR: `iter` method without an `IntoIterator` impl + todo!() + } + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> { + //~^ ERROR: `iter_mut` method without an `IntoIterator` impl + todo!() + } + } + } + { + struct S<T>(T); + impl<T> S<T> { + pub fn iter(&self) -> std::slice::Iter<'_, T> { + // Don't lint, there's an existing (wrong) IntoIterator impl + todo!() + } + } + + impl<'a, T> IntoIterator for &'a S<T> { + type Item = &'a String; + type IntoIter = std::slice::Iter<'a, String>; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } + { + struct S<T>(T); + impl<T> S<T> { + pub fn iter_mut(&self) -> std::slice::IterMut<'_, T> { + // Don't lint, there's an existing (wrong) IntoIterator impl + todo!() + } + } + + impl<'a, T> IntoIterator for &'a mut S<T> { + type Item = &'a mut String; + type IntoIter = std::slice::IterMut<'a, String>; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + } +} diff --git a/src/tools/clippy/tests/ui/iter_without_into_iter.stderr b/src/tools/clippy/tests/ui/iter_without_into_iter.stderr new file mode 100644 index 00000000000..9d0b99415a5 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_without_into_iter.stderr @@ -0,0 +1,150 @@ +error: `iter` method without an `IntoIterator` impl for `&S` + --> $DIR/iter_without_into_iter.rs:8:13 + | +LL | / pub fn iter(&self) -> std::slice::Iter<'_, u8> { +LL | | +LL | | [].iter() +LL | | } + | |_____________^ + | + = note: `-D clippy::iter-without-into-iter` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::iter_without_into_iter)]` +help: consider implementing `IntoIterator` for `&S` + | +LL ~ +LL + impl IntoIterator for &S { +LL + type IntoIter = std::slice::Iter<'_, u8>; +LL + type Iter = &u8; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: `iter_mut` method without an `IntoIterator` impl for `&mut S` + --> $DIR/iter_without_into_iter.rs:12:13 + | +LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> { +LL | | +LL | | [].iter_mut() +LL | | } + | |_____________^ + | +help: consider implementing `IntoIterator` for `&mut S` + | +LL ~ +LL + impl IntoIterator for &mut S { +LL + type IntoIter = std::slice::IterMut<'_, u8>; +LL + type Iter = &mut u8; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: `iter` method without an `IntoIterator` impl for `&S<'a>` + --> $DIR/iter_without_into_iter.rs:30:13 + | +LL | / pub fn iter(&self) -> std::slice::Iter<'_, u8> { +LL | | +LL | | self.0.iter() +LL | | } + | |_____________^ + | +help: consider implementing `IntoIterator` for `&S<'a>` + | +LL ~ +LL + impl IntoIterator for &S<'a> { +LL + type IntoIter = std::slice::Iter<'_, u8>; +LL + type Iter = &u8; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: `iter_mut` method without an `IntoIterator` impl for `&mut S<'a>` + --> $DIR/iter_without_into_iter.rs:34:13 + | +LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> { +LL | | +LL | | self.0.iter_mut() +LL | | } + | |_____________^ + | +help: consider implementing `IntoIterator` for `&mut S<'a>` + | +LL ~ +LL + impl IntoIterator for &mut S<'a> { +LL + type IntoIter = std::slice::IterMut<'_, u8>; +LL + type Iter = &mut u8; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: `iter` method without an `IntoIterator` impl for `&S5<T>` + --> $DIR/iter_without_into_iter.rs:68:13 + | +LL | / pub fn iter(&self) -> std::slice::Iter<'static, T> { +LL | | todo!() +LL | | } + | |_____________^ + | +help: consider implementing `IntoIterator` for `&S5<T>` + | +LL ~ +LL + impl IntoIterator for &S5<T> { +LL + type IntoIter = std::slice::Iter<'static, T>; +LL + type Iter = &T; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: `iter` method without an `IntoIterator` impl for `&S<T>` + --> $DIR/iter_without_into_iter.rs:76:13 + | +LL | / pub fn iter(&self) -> std::slice::Iter<'_, T> { +LL | | +LL | | todo!() +LL | | } + | |_____________^ + | +help: consider implementing `IntoIterator` for `&S<T>` + | +LL ~ +LL + impl IntoIterator for &S<T> { +LL + type IntoIter = std::slice::Iter<'_, T>; +LL + type Iter = &T; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: `iter_mut` method without an `IntoIterator` impl for `&mut S<T>` + --> $DIR/iter_without_into_iter.rs:80:13 + | +LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> { +LL | | +LL | | todo!() +LL | | } + | |_____________^ + | +help: consider implementing `IntoIterator` for `&mut S<T>` + | +LL ~ +LL + impl IntoIterator for &mut S<T> { +LL + type IntoIter = std::slice::IterMut<'_, T>; +LL + type Iter = &mut T; +LL + fn into_iter() -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/let_underscore_future.rs b/src/tools/clippy/tests/ui/let_underscore_future.rs index 873ae667ab7..c2185e9785d 100644 --- a/src/tools/clippy/tests/ui/let_underscore_future.rs +++ b/src/tools/clippy/tests/ui/let_underscore_future.rs @@ -9,8 +9,6 @@ fn custom() -> impl Future<Output = ()> { } fn do_something_to_future(future: &mut impl Future<Output = ()>) {} -//~^ ERROR: this argument is a mutable reference, but not used mutably -//~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` fn main() { let _ = some_async_fn(); diff --git a/src/tools/clippy/tests/ui/let_underscore_future.stderr b/src/tools/clippy/tests/ui/let_underscore_future.stderr index 3ba99c6377b..ef927a8083b 100644 --- a/src/tools/clippy/tests/ui/let_underscore_future.stderr +++ b/src/tools/clippy/tests/ui/let_underscore_future.stderr @@ -1,5 +1,5 @@ error: non-binding `let` on a future - --> $DIR/let_underscore_future.rs:16:5 + --> $DIR/let_underscore_future.rs:14:5 | LL | let _ = some_async_fn(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = some_async_fn(); = help: to override `-D warnings` add `#[allow(clippy::let_underscore_future)]` error: non-binding `let` on a future - --> $DIR/let_underscore_future.rs:18:5 + --> $DIR/let_underscore_future.rs:16:5 | LL | let _ = custom(); | ^^^^^^^^^^^^^^^^^ @@ -17,21 +17,12 @@ LL | let _ = custom(); = help: consider awaiting the future or dropping explicitly with `std::mem::drop` error: non-binding `let` on a future - --> $DIR/let_underscore_future.rs:23:5 + --> $DIR/let_underscore_future.rs:21:5 | LL | let _ = future; | ^^^^^^^^^^^^^^^ | = help: consider awaiting the future or dropping explicitly with `std::mem::drop` -error: this argument is a mutable reference, but not used mutably - --> $DIR/let_underscore_future.rs:11:35 - | -LL | fn do_something_to_future(future: &mut impl Future<Output = ()>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&impl Future<Output = ()>` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/manual_hash_one.fixed b/src/tools/clippy/tests/ui/manual_hash_one.fixed new file mode 100644 index 00000000000..edfd9c4a47b --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_hash_one.fixed @@ -0,0 +1,89 @@ +#![warn(clippy::manual_hash_one)] +#![allow(clippy::needless_borrows_for_generic_args)] + +use std::hash::{BuildHasher, Hash, Hasher}; + +fn returned(b: impl BuildHasher) -> u64 { + + + b.hash_one(&true) +} + +fn unsized_receiver(b: impl BuildHasher, s: &str) { + + + let _ = b.hash_one(&s[4..10]); +} + +fn owned_value(b: impl BuildHasher, v: Vec<u32>) -> Vec<u32> { + + + let _ = b.hash_one(&v); + v +} + +fn reused_hasher(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + let _ = hasher.finish(); + let _ = hasher.finish(); +} + +fn reused_hasher_in_return(b: impl BuildHasher) -> u64 { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + let _ = hasher.finish(); + hasher.finish() +} + +fn no_hash(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + let _ = hasher.finish(); +} + +fn hash_twice(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + true.hash(&mut hasher); + let _ = hasher.finish(); +} + +fn other_hasher(b: impl BuildHasher) { + let mut other_hasher = b.build_hasher(); + + let mut hasher = b.build_hasher(); + true.hash(&mut other_hasher); + let _ = hasher.finish(); +} + +fn finish_then_hash(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + let _ = hasher.finish(); + true.hash(&mut hasher); +} + +fn in_macro(b: impl BuildHasher) { + macro_rules! m { + ($b:expr) => {{ + let mut hasher = $b.build_hasher(); + true.hash(&mut hasher); + let _ = hasher.finish(); + }}; + } + + m!(b); +} + +#[clippy::msrv = "1.70"] +fn msrv_1_70(b: impl BuildHasher, v: impl Hash) { + let mut hasher = b.build_hasher(); + v.hash(&mut hasher); + let _ = hasher.finish(); +} + +#[clippy::msrv = "1.71"] +fn msrv_1_71(b: impl BuildHasher, v: impl Hash) { + + + let _ = b.hash_one(&v); +} diff --git a/src/tools/clippy/tests/ui/manual_hash_one.rs b/src/tools/clippy/tests/ui/manual_hash_one.rs new file mode 100644 index 00000000000..ee61522853f --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_hash_one.rs @@ -0,0 +1,89 @@ +#![warn(clippy::manual_hash_one)] +#![allow(clippy::needless_borrows_for_generic_args)] + +use std::hash::{BuildHasher, Hash, Hasher}; + +fn returned(b: impl BuildHasher) -> u64 { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + hasher.finish() +} + +fn unsized_receiver(b: impl BuildHasher, s: &str) { + let mut hasher = b.build_hasher(); + s[4..10].hash(&mut hasher); + let _ = hasher.finish(); +} + +fn owned_value(b: impl BuildHasher, v: Vec<u32>) -> Vec<u32> { + let mut hasher = b.build_hasher(); + v.hash(&mut hasher); + let _ = hasher.finish(); + v +} + +fn reused_hasher(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + let _ = hasher.finish(); + let _ = hasher.finish(); +} + +fn reused_hasher_in_return(b: impl BuildHasher) -> u64 { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + let _ = hasher.finish(); + hasher.finish() +} + +fn no_hash(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + let _ = hasher.finish(); +} + +fn hash_twice(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + true.hash(&mut hasher); + true.hash(&mut hasher); + let _ = hasher.finish(); +} + +fn other_hasher(b: impl BuildHasher) { + let mut other_hasher = b.build_hasher(); + + let mut hasher = b.build_hasher(); + true.hash(&mut other_hasher); + let _ = hasher.finish(); +} + +fn finish_then_hash(b: impl BuildHasher) { + let mut hasher = b.build_hasher(); + let _ = hasher.finish(); + true.hash(&mut hasher); +} + +fn in_macro(b: impl BuildHasher) { + macro_rules! m { + ($b:expr) => {{ + let mut hasher = $b.build_hasher(); + true.hash(&mut hasher); + let _ = hasher.finish(); + }}; + } + + m!(b); +} + +#[clippy::msrv = "1.70"] +fn msrv_1_70(b: impl BuildHasher, v: impl Hash) { + let mut hasher = b.build_hasher(); + v.hash(&mut hasher); + let _ = hasher.finish(); +} + +#[clippy::msrv = "1.71"] +fn msrv_1_71(b: impl BuildHasher, v: impl Hash) { + let mut hasher = b.build_hasher(); + v.hash(&mut hasher); + let _ = hasher.finish(); +} diff --git a/src/tools/clippy/tests/ui/manual_hash_one.stderr b/src/tools/clippy/tests/ui/manual_hash_one.stderr new file mode 100644 index 00000000000..3ce6f41e1f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_hash_one.stderr @@ -0,0 +1,56 @@ +error: manual implementation of `BuildHasher::hash_one` + --> $DIR/manual_hash_one.rs:9:5 + | +LL | hasher.finish() + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-hash-one` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_hash_one)]` +help: try + | +LL ~ +LL ~ +LL ~ b.hash_one(&true) + | + +error: manual implementation of `BuildHasher::hash_one` + --> $DIR/manual_hash_one.rs:15:13 + | +LL | let _ = hasher.finish(); + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ +LL ~ +LL ~ let _ = b.hash_one(&s[4..10]); + | + +error: manual implementation of `BuildHasher::hash_one` + --> $DIR/manual_hash_one.rs:21:13 + | +LL | let _ = hasher.finish(); + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ +LL ~ +LL ~ let _ = b.hash_one(&v); + | + +error: manual implementation of `BuildHasher::hash_one` + --> $DIR/manual_hash_one.rs:88:13 + | +LL | let _ = hasher.finish(); + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ +LL ~ +LL ~ let _ = b.hash_one(&v); + | + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.fixed b/src/tools/clippy/tests/ui/manual_let_else_match.fixed index 09b713f0410..588ba5edd8f 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.fixed +++ b/src/tools/clippy/tests/ui/manual_let_else_match.fixed @@ -133,3 +133,7 @@ fn not_fire() { [data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ ..] => data, }; } + +fn issue11579() { + let Some(msg) = Some("hi") else { unreachable!("can't happen") }; +} diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.rs b/src/tools/clippy/tests/ui/manual_let_else_match.rs index e6af4738420..c37b5613ff7 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.rs +++ b/src/tools/clippy/tests/ui/manual_let_else_match.rs @@ -170,3 +170,11 @@ fn not_fire() { [data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ ..] => data, }; } + +fn issue11579() { + let msg = match Some("hi") { + //~^ ERROR: this could be rewritten as `let...else` + Some(m) => m, + _ => unreachable!("can't happen"), + }; +} diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.stderr b/src/tools/clippy/tests/ui/manual_let_else_match.stderr index 8ca2c84072d..18bfe324ba7 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else_match.stderr @@ -92,5 +92,15 @@ LL | | _ => return, LL | | }; | |______^ help: consider writing: `let ([data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ .., 0]) = data.as_slice() else { return };` -error: aborting due to 9 previous errors +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:175:5 + | +LL | / let msg = match Some("hi") { +LL | | +LL | | Some(m) => m, +LL | | _ => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(msg) = Some("hi") else { unreachable!("can't happen") };` + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs index 0e439dabfd6..e32ba863176 100644 --- a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs +++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs @@ -10,10 +10,9 @@ enum E { _C, } -// user forgot to remove the marker +// if the user explicitly marks as nonexhaustive we shouldn't warn them #[non_exhaustive] enum Ep { - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern A, B, #[doc(hidden)] diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr index ce7e21c94bb..7361a4a2cbb 100644 --- a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr +++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr @@ -22,23 +22,5 @@ LL | _C, = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_non_exhaustive)]` -error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive_enum.rs:15:1 - | -LL | / enum Ep { -LL | | -LL | | A, -LL | | B, -LL | | #[doc(hidden)] -LL | | _C, -LL | | } - | |_^ - | -help: remove this variant - --> $DIR/manual_non_exhaustive_enum.rs:20:5 - | -LL | _C, - | ^^ - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/mut_key.rs b/src/tools/clippy/tests/ui/mut_key.rs index 80692132176..2d70bfd4c77 100644 --- a/src/tools/clippy/tests/ui/mut_key.rs +++ b/src/tools/clippy/tests/ui/mut_key.rs @@ -32,8 +32,6 @@ fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<K //~^ ERROR: mutable key type //~| NOTE: `-D clippy::mutable-key-type` implied by `-D warnings` //~| ERROR: mutable key type - //~| ERROR: this argument is a mutable reference, but not used mutably - //~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` let _other: HashMap<Key, bool> = HashMap::new(); //~^ ERROR: mutable key type m.keys().cloned().collect() diff --git a/src/tools/clippy/tests/ui/mut_key.stderr b/src/tools/clippy/tests/ui/mut_key.stderr index 3701769a9ca..48eeaff78a7 100644 --- a/src/tools/clippy/tests/ui/mut_key.stderr +++ b/src/tools/clippy/tests/ui/mut_key.stderr @@ -14,103 +14,94 @@ LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> Hash | ^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:37:5 + --> $DIR/mut_key.rs:35:5 | LL | let _other: HashMap<Key, bool> = HashMap::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:65:22 + --> $DIR/mut_key.rs:63:22 | LL | fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:78:5 + --> $DIR/mut_key.rs:76:5 | LL | let _map = HashMap::<Cell<usize>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:80:5 + --> $DIR/mut_key.rs:78:5 | LL | let _map = HashMap::<&mut Cell<usize>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:82:5 + --> $DIR/mut_key.rs:80:5 | LL | let _map = HashMap::<&mut usize, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:85:5 + --> $DIR/mut_key.rs:83:5 | LL | let _map = HashMap::<Vec<Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:87:5 + --> $DIR/mut_key.rs:85:5 | LL | let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:89:5 + --> $DIR/mut_key.rs:87:5 | LL | let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:91:5 + --> $DIR/mut_key.rs:89:5 | LL | let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:93:5 + --> $DIR/mut_key.rs:91:5 | LL | let _map = HashMap::<Option<Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:95:5 + --> $DIR/mut_key.rs:93:5 | LL | let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:97:5 + --> $DIR/mut_key.rs:95:5 | LL | let _map = HashMap::<Result<&mut usize, ()>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:100:5 + --> $DIR/mut_key.rs:98:5 | LL | let _map = HashMap::<Box<Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:102:5 + --> $DIR/mut_key.rs:100:5 | LL | let _map = HashMap::<Rc<Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: mutable key type - --> $DIR/mut_key.rs:104:5 + --> $DIR/mut_key.rs:102:5 | LL | let _map = HashMap::<Arc<Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this argument is a mutable reference, but not used mutably - --> $DIR/mut_key.rs:31:32 - | -LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> { - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&HashMap<Key, usize>` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` - -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/mut_reference.rs b/src/tools/clippy/tests/ui/mut_reference.rs index f3db226e4e7..1d7faaa5e75 100644 --- a/src/tools/clippy/tests/ui/mut_reference.rs +++ b/src/tools/clippy/tests/ui/mut_reference.rs @@ -22,8 +22,6 @@ impl MyStruct { fn takes_an_immutable_reference(&self, a: &i32) {} fn takes_a_mutable_reference(&self, a: &mut i32) {} - //~^ ERROR: this argument is a mutable reference, but not used mutably - //~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` } #[warn(clippy::unnecessary_mut_passed)] diff --git a/src/tools/clippy/tests/ui/mut_reference.stderr b/src/tools/clippy/tests/ui/mut_reference.stderr index d7a0d0c2252..87db08e2a74 100644 --- a/src/tools/clippy/tests/ui/mut_reference.stderr +++ b/src/tools/clippy/tests/ui/mut_reference.stderr @@ -1,5 +1,5 @@ error: the function `takes_an_immutable_reference` doesn't need a mutable reference - --> $DIR/mut_reference.rs:32:34 + --> $DIR/mut_reference.rs:30:34 | LL | takes_an_immutable_reference(&mut 42); | ^^^^^^^ @@ -8,25 +8,16 @@ LL | takes_an_immutable_reference(&mut 42); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` error: the function `as_ptr` doesn't need a mutable reference - --> $DIR/mut_reference.rs:36:12 + --> $DIR/mut_reference.rs:34:12 | LL | as_ptr(&mut 42); | ^^^^^^^ error: the method `takes_an_immutable_reference` doesn't need a mutable reference - --> $DIR/mut_reference.rs:41:44 + --> $DIR/mut_reference.rs:39:44 | LL | my_struct.takes_an_immutable_reference(&mut 42); | ^^^^^^^ -error: this argument is a mutable reference, but not used mutably - --> $DIR/mut_reference.rs:24:44 - | -LL | fn takes_a_mutable_reference(&self, a: &mut i32) {} - | ^^^^^^^^ help: consider changing to: `&i32` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs index 9cddcb3df23..39d76f99900 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs @@ -1,4 +1,5 @@ #![allow(clippy::if_same_then_else, clippy::no_effect, clippy::redundant_closure_call)] +#![warn(clippy::needless_pass_by_ref_mut)] #![feature(lint_reasons)] //@no-rustfix use std::ptr::NonNull; diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr index 0c7fbd5df6d..aa937c3f6af 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr +++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr @@ -1,5 +1,5 @@ error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:6:11 + --> $DIR/needless_pass_by_ref_mut.rs:7:11 | LL | fn foo(s: &mut Vec<u32>, b: &u32, x: &mut u32) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec<u32>` @@ -8,79 +8,79 @@ LL | fn foo(s: &mut Vec<u32>, b: &u32, x: &mut u32) { = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:31:12 + --> $DIR/needless_pass_by_ref_mut.rs:32:12 | LL | fn foo6(s: &mut Vec<u32>) { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec<u32>` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:44:29 + --> $DIR/needless_pass_by_ref_mut.rs:45:29 | LL | fn mushroom(&self, vec: &mut Vec<i32>) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec<i32>` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:49:31 + --> $DIR/needless_pass_by_ref_mut.rs:50:31 | LL | fn badger(&mut self, vec: &mut Vec<i32>) -> usize { | ^^^^^^^^^^^^^ help: consider changing to: `&Vec<i32>` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:126:16 + --> $DIR/needless_pass_by_ref_mut.rs:127:16 | LL | async fn a1(x: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:130:16 + --> $DIR/needless_pass_by_ref_mut.rs:131:16 | LL | async fn a2(x: &mut i32, y: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:134:16 + --> $DIR/needless_pass_by_ref_mut.rs:135:16 | LL | async fn a3(x: &mut i32, y: String, z: String) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:138:16 + --> $DIR/needless_pass_by_ref_mut.rs:139:16 | LL | async fn a4(x: &mut i32, y: i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:142:24 + --> $DIR/needless_pass_by_ref_mut.rs:143:24 | LL | async fn a5(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:146:24 + --> $DIR/needless_pass_by_ref_mut.rs:147:24 | LL | async fn a6(x: i32, y: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:150:32 + --> $DIR/needless_pass_by_ref_mut.rs:151:32 | LL | async fn a7(x: i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:154:24 + --> $DIR/needless_pass_by_ref_mut.rs:155:24 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:154:45 + --> $DIR/needless_pass_by_ref_mut.rs:155:45 | LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:188:16 + --> $DIR/needless_pass_by_ref_mut.rs:189:16 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -88,7 +88,7 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:194:20 + --> $DIR/needless_pass_by_ref_mut.rs:195:20 | LL | fn cfg_warn(s: &mut u32) {} | ^^^^^^^^ help: consider changing to: `&u32` @@ -96,19 +96,19 @@ LL | fn cfg_warn(s: &mut u32) {} = note: this is cfg-gated and may require further changes error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:208:39 + --> $DIR/needless_pass_by_ref_mut.rs:209:39 | LL | async fn inner_async2(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&u32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:216:26 + --> $DIR/needless_pass_by_ref_mut.rs:217:26 | LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:235:34 + --> $DIR/needless_pass_by_ref_mut.rs:236:34 | LL | pub async fn call_in_closure1(n: &mut str) { | ^^^^^^^^ help: consider changing to: `&str` @@ -116,7 +116,7 @@ LL | pub async fn call_in_closure1(n: &mut str) { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:247:25 + --> $DIR/needless_pass_by_ref_mut.rs:248:25 | LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { | ^^^^^^^^^^ help: consider changing to: `&usize` @@ -124,7 +124,7 @@ LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:254:20 + --> $DIR/needless_pass_by_ref_mut.rs:255:20 | LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { | ^^^^^^^^^^ help: consider changing to: `&usize` @@ -132,7 +132,7 @@ LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:265:26 + --> $DIR/needless_pass_by_ref_mut.rs:266:26 | LL | pub async fn closure4(n: &mut usize) { | ^^^^^^^^^^ help: consider changing to: `&usize` diff --git a/src/tools/clippy/tests/ui/needless_raw_string.fixed b/src/tools/clippy/tests/ui/needless_raw_string.fixed index 85549810513..4ea711fd67a 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string.fixed +++ b/src/tools/clippy/tests/ui/needless_raw_string.fixed @@ -18,4 +18,8 @@ fn main() { multiline string "; + + "no hashes"; + b"no hashes"; + c"no hashes"; } diff --git a/src/tools/clippy/tests/ui/needless_raw_string.rs b/src/tools/clippy/tests/ui/needless_raw_string.rs index 06d49730387..b6239f9b1f0 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string.rs +++ b/src/tools/clippy/tests/ui/needless_raw_string.rs @@ -18,4 +18,8 @@ fn main() { multiline string "#; + + r"no hashes"; + br"no hashes"; + cr"no hashes"; } diff --git a/src/tools/clippy/tests/ui/needless_raw_string.stderr b/src/tools/clippy/tests/ui/needless_raw_string.stderr index e6806b31b1d..276bc84c6c3 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string.stderr +++ b/src/tools/clippy/tests/ui/needless_raw_string.stderr @@ -6,7 +6,7 @@ LL | r#"aaa"#; | = note: `-D clippy::needless-raw-strings` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]` -help: try +help: use a plain string literal instead | LL - r#"aaa"#; LL + "aaa"; @@ -18,7 +18,7 @@ error: unnecessary raw string literal LL | br#"aaa"#; | ^^^^^^^^^ | -help: try +help: use a plain byte string literal instead | LL - br#"aaa"#; LL + b"aaa"; @@ -30,7 +30,7 @@ error: unnecessary raw string literal LL | cr#"aaa"#; | ^^^^^^^^^ | -help: try +help: use a plain C string literal instead | LL - cr#"aaa"#; LL + c"aaa"; @@ -46,7 +46,7 @@ LL | | string LL | | "#; | |______^ | -help: try +help: use a plain string literal instead | LL ~ " LL | a @@ -55,5 +55,41 @@ LL | string LL ~ "; | -error: aborting due to 4 previous errors +error: unnecessary raw string literal + --> $DIR/needless_raw_string.rs:22:5 + | +LL | r"no hashes"; + | ^^^^^^^^^^^^ + | +help: use a plain string literal instead + | +LL - r"no hashes"; +LL + "no hashes"; + | + +error: unnecessary raw string literal + --> $DIR/needless_raw_string.rs:23:5 + | +LL | br"no hashes"; + | ^^^^^^^^^^^^^ + | +help: use a plain byte string literal instead + | +LL - br"no hashes"; +LL + b"no hashes"; + | + +error: unnecessary raw string literal + --> $DIR/needless_raw_string.rs:24:5 + | +LL | cr"no hashes"; + | ^^^^^^^^^^^^^ + | +help: use a plain C string literal instead + | +LL - cr"no hashes"; +LL + c"no hashes"; + | + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr index 4399c6555c2..017160b1a42 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr +++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr @@ -6,7 +6,7 @@ LL | r#"\aaa"#; | = note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_raw_string_hashes)]` -help: remove all the hashes around the literal +help: remove all the hashes around the string literal | LL - r#"\aaa"#; LL + r"\aaa"; @@ -18,7 +18,7 @@ error: unnecessary hashes around raw string literal LL | r##"Hello "world"!"##; | ^^^^^^^^^^^^^^^^^^^^^ | -help: remove one hash from both sides of the literal +help: remove one hash from both sides of the string literal | LL - r##"Hello "world"!"##; LL + r#"Hello "world"!"#; @@ -30,7 +30,7 @@ error: unnecessary hashes around raw string literal LL | r######" "### "## "# "######; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: remove 2 hashes from both sides of the literal +help: remove 2 hashes from both sides of the string literal | LL - r######" "### "## "# "######; LL + r####" "### "## "# "####; @@ -42,7 +42,7 @@ error: unnecessary hashes around raw string literal LL | r######" "aa" "# "## "######; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: remove 3 hashes from both sides of the literal +help: remove 3 hashes from both sides of the string literal | LL - r######" "aa" "# "## "######; LL + r###" "aa" "# "## "###; @@ -54,7 +54,7 @@ error: unnecessary hashes around raw string literal LL | br#"\aaa"#; | ^^^^^^^^^^ | -help: remove all the hashes around the literal +help: remove all the hashes around the byte string literal | LL - br#"\aaa"#; LL + br"\aaa"; @@ -66,7 +66,7 @@ error: unnecessary hashes around raw string literal LL | br##"Hello "world"!"##; | ^^^^^^^^^^^^^^^^^^^^^^ | -help: remove one hash from both sides of the literal +help: remove one hash from both sides of the byte string literal | LL - br##"Hello "world"!"##; LL + br#"Hello "world"!"#; @@ -78,7 +78,7 @@ error: unnecessary hashes around raw string literal LL | br######" "### "## "# "######; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: remove 2 hashes from both sides of the literal +help: remove 2 hashes from both sides of the byte string literal | LL - br######" "### "## "# "######; LL + br####" "### "## "# "####; @@ -90,7 +90,7 @@ error: unnecessary hashes around raw string literal LL | br######" "aa" "# "## "######; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: remove 3 hashes from both sides of the literal +help: remove 3 hashes from both sides of the byte string literal | LL - br######" "aa" "# "## "######; LL + br###" "aa" "# "## "###; @@ -102,7 +102,7 @@ error: unnecessary hashes around raw string literal LL | cr#"\aaa"#; | ^^^^^^^^^^ | -help: remove all the hashes around the literal +help: remove all the hashes around the C string literal | LL - cr#"\aaa"#; LL + cr"\aaa"; @@ -114,7 +114,7 @@ error: unnecessary hashes around raw string literal LL | cr##"Hello "world"!"##; | ^^^^^^^^^^^^^^^^^^^^^^ | -help: remove one hash from both sides of the literal +help: remove one hash from both sides of the C string literal | LL - cr##"Hello "world"!"##; LL + cr#"Hello "world"!"#; @@ -126,7 +126,7 @@ error: unnecessary hashes around raw string literal LL | cr######" "### "## "# "######; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: remove 2 hashes from both sides of the literal +help: remove 2 hashes from both sides of the C string literal | LL - cr######" "### "## "# "######; LL + cr####" "### "## "# "####; @@ -138,7 +138,7 @@ error: unnecessary hashes around raw string literal LL | cr######" "aa" "# "## "######; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: remove 3 hashes from both sides of the literal +help: remove 3 hashes from both sides of the C string literal | LL - cr######" "aa" "# "## "######; LL + cr###" "aa" "# "## "###; @@ -154,7 +154,7 @@ LL | | string LL | | "#; | |______^ | -help: remove all the hashes around the literal +help: remove all the hashes around the string literal | LL ~ r" LL | \a @@ -169,7 +169,7 @@ error: unnecessary hashes around raw string literal LL | r###"rust"###; | ^^^^^^^^^^^^^ | -help: remove all the hashes around the literal +help: remove all the hashes around the string literal | LL - r###"rust"###; LL + r"rust"; @@ -181,7 +181,7 @@ error: unnecessary hashes around raw string literal LL | r#"hello world"#; | ^^^^^^^^^^^^^^^^ | -help: remove all the hashes around the literal +help: remove all the hashes around the string literal | LL - r#"hello world"#; LL + r"hello world"; diff --git a/src/tools/clippy/tests/ui/print_literal.fixed b/src/tools/clippy/tests/ui/print_literal.fixed index 88cd3a54b41..a7157c07f8a 100644 --- a/src/tools/clippy/tests/ui/print_literal.fixed +++ b/src/tools/clippy/tests/ui/print_literal.fixed @@ -39,20 +39,30 @@ fn main() { // throw a warning println!("hello world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string println!("world hello"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // named args shouldn't change anything either println!("hello world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string println!("world hello"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // The string literal from `file!()` has a callsite span that isn't marked as coming from an // expansion println!("file: {}", file!()); + + // Braces in unicode escapes should not be escaped + println!("{{}} \x00 \u{ab123} \\\u{ab123} {{:?}}"); + println!("\\\u{1234}"); + // This does not lint because it would have to suggest unescaping the character + println!(r"{}", "\u{ab123}"); + // These are not unicode escapes + println!("\\u{{ab123}} \\u{{{{"); + println!(r"\u{{ab123}} \u{{{{"); + println!("\\{{ab123}} \\u{{{{"); + println!("\\u{{ab123}}"); + println!("\\\\u{{1234}}"); + + println!("mixed: {{hello}} {world}"); } diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs index bd7444c9606..4b04b42744c 100644 --- a/src/tools/clippy/tests/ui/print_literal.rs +++ b/src/tools/clippy/tests/ui/print_literal.rs @@ -39,20 +39,30 @@ fn main() { // throw a warning println!("{0} {1}", "hello", "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string println!("{1} {0}", "hello", "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // named args shouldn't change anything either println!("{foo} {bar}", foo = "hello", bar = "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string println!("{bar} {foo}", foo = "hello", bar = "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // The string literal from `file!()` has a callsite span that isn't marked as coming from an // expansion println!("file: {}", file!()); + + // Braces in unicode escapes should not be escaped + println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}"); + println!("{}", "\\\u{1234}"); + // This does not lint because it would have to suggest unescaping the character + println!(r"{}", "\u{ab123}"); + // These are not unicode escapes + println!("{}", r"\u{ab123} \u{{"); + println!(r"{}", r"\u{ab123} \u{{"); + println!("{}", r"\{ab123} \u{{"); + println!("{}", "\\u{ab123}"); + println!("{}", "\\\\u{1234}"); + + println!("mixed: {} {world}", "{hello}"); } diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr index 1d9751b92e9..8c011d7bc0a 100644 --- a/src/tools/clippy/tests/ui/print_literal.stderr +++ b/src/tools/clippy/tests/ui/print_literal.stderr @@ -52,97 +52,145 @@ error: literal with an empty format string --> $DIR/print_literal.rs:40:25 | LL | println!("{0} {1}", "hello", "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^ | help: try | LL - println!("{0} {1}", "hello", "world"); -LL + println!("hello {1}", "world"); +LL + println!("hello world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:40:34 + --> $DIR/print_literal.rs:42:25 | -LL | println!("{0} {1}", "hello", "world"); - | ^^^^^^^ +LL | println!("{1} {0}", "hello", "world"); + | ^^^^^^^^^^^^^^^^ | help: try | -LL - println!("{0} {1}", "hello", "world"); -LL + println!("{0} world", "hello"); +LL - println!("{1} {0}", "hello", "world"); +LL + println!("world hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:43:34 + --> $DIR/print_literal.rs:46:35 | -LL | println!("{1} {0}", "hello", "world"); - | ^^^^^^^ +LL | println!("{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^^^^^^^^^^^^^^^^ | help: try | -LL - println!("{1} {0}", "hello", "world"); -LL + println!("world {0}", "hello"); +LL - println!("{foo} {bar}", foo = "hello", bar = "world"); +LL + println!("hello world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:43:25 + --> $DIR/print_literal.rs:48:35 | -LL | println!("{1} {0}", "hello", "world"); - | ^^^^^^^ +LL | println!("{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^^^^^^^^^^^^^^^^ | help: try | -LL - println!("{1} {0}", "hello", "world"); -LL + println!("{1} hello", "world"); +LL - println!("{bar} {foo}", foo = "hello", bar = "world"); +LL + println!("world hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:48:35 + --> $DIR/print_literal.rs:56:20 | -LL | println!("{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ +LL | println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: try | -LL - println!("{foo} {bar}", foo = "hello", bar = "world"); -LL + println!("hello {bar}", bar = "world"); +LL - println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}"); +LL + println!("{{}} \x00 \u{ab123} \\\u{ab123} {{:?}}"); | error: literal with an empty format string - --> $DIR/print_literal.rs:48:50 + --> $DIR/print_literal.rs:57:20 | -LL | println!("{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ +LL | println!("{}", "\\\u{1234}"); + | ^^^^^^^^^^^^ | help: try | -LL - println!("{foo} {bar}", foo = "hello", bar = "world"); -LL + println!("{foo} world", foo = "hello"); +LL - println!("{}", "\\\u{1234}"); +LL + println!("\\\u{1234}"); | error: literal with an empty format string - --> $DIR/print_literal.rs:51:50 + --> $DIR/print_literal.rs:61:20 | -LL | println!("{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ +LL | println!("{}", r"\u{ab123} \u{{"); + | ^^^^^^^^^^^^^^^^^ | help: try | -LL - println!("{bar} {foo}", foo = "hello", bar = "world"); -LL + println!("world {foo}", foo = "hello"); +LL - println!("{}", r"\u{ab123} \u{{"); +LL + println!("\\u{{ab123}} \\u{{{{"); | error: literal with an empty format string - --> $DIR/print_literal.rs:51:35 + --> $DIR/print_literal.rs:62:21 | -LL | println!("{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ +LL | println!(r"{}", r"\u{ab123} \u{{"); + | ^^^^^^^^^^^^^^^^^ | help: try | -LL - println!("{bar} {foo}", foo = "hello", bar = "world"); -LL + println!("{bar} hello", bar = "world"); +LL - println!(r"{}", r"\u{ab123} \u{{"); +LL + println!(r"\u{{ab123}} \u{{{{"); + | + +error: literal with an empty format string + --> $DIR/print_literal.rs:63:20 + | +LL | println!("{}", r"\{ab123} \u{{"); + | ^^^^^^^^^^^^^^^^ + | +help: try + | +LL - println!("{}", r"\{ab123} \u{{"); +LL + println!("\\{{ab123}} \\u{{{{"); + | + +error: literal with an empty format string + --> $DIR/print_literal.rs:64:20 + | +LL | println!("{}", "\\u{ab123}"); + | ^^^^^^^^^^^^ + | +help: try + | +LL - println!("{}", "\\u{ab123}"); +LL + println!("\\u{{ab123}}"); + | + +error: literal with an empty format string + --> $DIR/print_literal.rs:65:20 + | +LL | println!("{}", "\\\\u{1234}"); + | ^^^^^^^^^^^^^ + | +help: try + | +LL - println!("{}", "\\\\u{1234}"); +LL + println!("\\\\u{{1234}}"); + | + +error: literal with an empty format string + --> $DIR/print_literal.rs:67:35 + | +LL | println!("mixed: {} {world}", "{hello}"); + | ^^^^^^^^^ + | +help: try + | +LL - println!("mixed: {} {world}", "{hello}"); +LL + println!("mixed: {{hello}} {world}"); | -error: aborting due to 12 previous errors +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_locals.rs b/src/tools/clippy/tests/ui/redundant_locals.rs index c5d93e4365d..e81db300f15 100644 --- a/src/tools/clippy/tests/ui/redundant_locals.rs +++ b/src/tools/clippy/tests/ui/redundant_locals.rs @@ -118,3 +118,40 @@ fn macros() { let x = x; } } + +struct WithDrop(usize); +impl Drop for WithDrop { + fn drop(&mut self) {} +} + +struct InnerDrop(WithDrop); + +struct ComposeDrop { + d: WithDrop, +} + +struct WithoutDrop(usize); + +fn drop_trait() { + let a = WithDrop(1); + let b = WithDrop(2); + let a = a; +} + +fn without_drop() { + let a = WithoutDrop(1); + let b = WithoutDrop(2); + let a = a; +} + +fn drop_inner() { + let a = InnerDrop(WithDrop(1)); + let b = InnerDrop(WithDrop(2)); + let a = a; +} + +fn drop_compose() { + let a = ComposeDrop { d: WithDrop(1) }; + let b = ComposeDrop { d: WithDrop(1) }; + let a = a; +} diff --git a/src/tools/clippy/tests/ui/redundant_locals.stderr b/src/tools/clippy/tests/ui/redundant_locals.stderr index 13b872e9576..d794a87fe7d 100644 --- a/src/tools/clippy/tests/ui/redundant_locals.stderr +++ b/src/tools/clippy/tests/ui/redundant_locals.stderr @@ -1,137 +1,172 @@ -error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:11:9 +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:12:5 | -LL | let x = 1; - | ^ LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` +help: `x` is initially defined here + --> $DIR/redundant_locals.rs:11:9 + | +LL | let x = 1; + | ^ = note: `-D clippy::redundant-locals` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::redundant_locals)]` -error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:16:9 +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:17:5 | -LL | let mut x = 1; - | ^^^^^ LL | let mut x = x; | ^^^^^^^^^^^^^^ | - = help: remove the redefinition of `x` +help: `x` is initially defined here + --> $DIR/redundant_locals.rs:16:9 + | +LL | let mut x = 1; + | ^^^^^ -error: redundant redefinition of a binding +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:47:5 + | +LL | let x = x; + | ^^^^^^^^^^ + | +help: `x` is initially defined here --> $DIR/redundant_locals.rs:46:14 | LL | fn parameter(x: i32) { | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:52:5 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:51:9 | LL | let x = 1; | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:53:5 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:52:9 | LL | let x = x; | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:54:5 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:53:9 | LL | let x = x; | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:55:5 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:54:9 | LL | let x = x; | ^ -LL | let x = x; + +error: redundant redefinition of a binding `a` + --> $DIR/redundant_locals.rs:61:5 + | +LL | let a = a; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `a` is initially defined here --> $DIR/redundant_locals.rs:59:9 | LL | let a = 1; | ^ -LL | let b = 2; -LL | let a = a; + +error: redundant redefinition of a binding `b` + --> $DIR/redundant_locals.rs:62:5 + | +LL | let b = b; | ^^^^^^^^^^ | - = help: remove the redefinition of `a` - -error: redundant redefinition of a binding +help: `b` is initially defined here --> $DIR/redundant_locals.rs:60:9 | LL | let b = 2; | ^ -LL | let a = a; -LL | let b = b; - | ^^^^^^^^^^ - | - = help: remove the redefinition of `b` -error: redundant redefinition of a binding +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:68:9 + | +LL | let x = x; + | ^^^^^^^^^^ + | +help: `x` is initially defined here --> $DIR/redundant_locals.rs:67:13 | LL | let x = 1; | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:75:9 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:74:13 | LL | let x = 1; | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:78:9 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:77:6 | LL | |x: i32| { | ^ + +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:97:9 + | LL | let x = x; | ^^^^^^^^^^ | - = help: remove the redefinition of `x` - -error: redundant redefinition of a binding +help: `x` is initially defined here --> $DIR/redundant_locals.rs:94:9 | LL | let x = 1; | ^ -... -LL | let x = x; - | ^^^^^^^^^^ + +error: redundant redefinition of a binding `a` + --> $DIR/redundant_locals.rs:144:5 + | +LL | let a = a; + | ^^^^^^^^^^ | - = help: remove the redefinition of `x` +help: `a` is initially defined here + --> $DIR/redundant_locals.rs:142:9 + | +LL | let a = WithoutDrop(1); + | ^ -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs index 550ec007268..33211b32d74 100644 --- a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs +++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.rs @@ -40,8 +40,6 @@ impl T { pub fn hash(&self, state: &mut T) { //~^ ERROR: method `hash` can be confused for the standard trait method `std::hash::Ha - //~| ERROR: this argument is a mutable reference, but not used mutably - //~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` unimplemented!() } diff --git a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr index 79afddea247..c257f411342 100644 --- a/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr +++ b/src/tools/clippy/tests/ui/should_impl_trait/method_list_2.stderr @@ -38,8 +38,6 @@ error: method `hash` can be confused for the standard trait method `std::hash::H | LL | / pub fn hash(&self, state: &mut T) { LL | | -LL | | -LL | | LL | | unimplemented!() LL | | } | |_____^ @@ -47,7 +45,7 @@ LL | | } = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name error: method `index` can be confused for the standard trait method `std::ops::Index::index` - --> $DIR/method_list_2.rs:48:5 + --> $DIR/method_list_2.rs:46:5 | LL | / pub fn index(&self, index: usize) -> &Self { LL | | @@ -58,7 +56,7 @@ LL | | } = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` - --> $DIR/method_list_2.rs:53:5 + --> $DIR/method_list_2.rs:51:5 | LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { LL | | @@ -69,7 +67,7 @@ LL | | } = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` - --> $DIR/method_list_2.rs:58:5 + --> $DIR/method_list_2.rs:56:5 | LL | / pub fn into_iter(self) -> Self { LL | | @@ -80,7 +78,7 @@ LL | | } = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` - --> $DIR/method_list_2.rs:63:5 + --> $DIR/method_list_2.rs:61:5 | LL | / pub fn mul(self, rhs: Self) -> Self { LL | | @@ -91,7 +89,7 @@ LL | | } = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` - --> $DIR/method_list_2.rs:68:5 + --> $DIR/method_list_2.rs:66:5 | LL | / pub fn neg(self) -> Self { LL | | @@ -102,7 +100,7 @@ LL | | } = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` - --> $DIR/method_list_2.rs:73:5 + --> $DIR/method_list_2.rs:71:5 | LL | / pub fn next(&mut self) -> Option<Self> { LL | | @@ -113,7 +111,7 @@ LL | | } = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name error: method `not` can be confused for the standard trait method `std::ops::Not::not` - --> $DIR/method_list_2.rs:78:5 + --> $DIR/method_list_2.rs:76:5 | LL | / pub fn not(self) -> Self { LL | | @@ -124,7 +122,7 @@ LL | | } = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` - --> $DIR/method_list_2.rs:83:5 + --> $DIR/method_list_2.rs:81:5 | LL | / pub fn rem(self, rhs: Self) -> Self { LL | | @@ -135,7 +133,7 @@ LL | | } = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` - --> $DIR/method_list_2.rs:88:5 + --> $DIR/method_list_2.rs:86:5 | LL | / pub fn shl(self, rhs: Self) -> Self { LL | | @@ -146,7 +144,7 @@ LL | | } = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` - --> $DIR/method_list_2.rs:93:5 + --> $DIR/method_list_2.rs:91:5 | LL | / pub fn shr(self, rhs: Self) -> Self { LL | | @@ -157,7 +155,7 @@ LL | | } = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` - --> $DIR/method_list_2.rs:98:5 + --> $DIR/method_list_2.rs:96:5 | LL | / pub fn sub(self, rhs: Self) -> Self { LL | | @@ -167,15 +165,5 @@ LL | | } | = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name -error: this argument is a mutable reference, but not used mutably - --> $DIR/method_list_2.rs:41:31 - | -LL | pub fn hash(&self, state: &mut T) { - | ^^^^^^ help: consider changing to: `&T` - | - = warning: changing this function will impact semver compatibility - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` - -error: aborting due to 16 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.rs b/src/tools/clippy/tests/ui/slow_vector_initialization.rs index 5c3086c9d69..16f81019574 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.rs +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.rs @@ -103,8 +103,6 @@ fn from_empty_vec() { } fn do_stuff(vec: &mut [u8]) {} -//~^ ERROR: this argument is a mutable reference, but not used mutably -//~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` fn extend_vector_with_manipulations_between() { let len = 300; diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr index 4d24400ecb5..16a7057653c 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr @@ -105,14 +105,5 @@ LL | vec1 = vec![]; LL | vec1.resize(10, 0); | ^^^^^^^^^^^^^^^^^^ -error: this argument is a mutable reference, but not used mutably - --> $DIR/slow_vector_initialization.rs:105:18 - | -LL | fn do_stuff(vec: &mut [u8]) {} - | ^^^^^^^^^ help: consider changing to: `&[u8]` - | - = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` - -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed index 8027c053fb5..a7555704fbe 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -1,8 +1,12 @@ +//@aux-build:proc_macro_derive.rs #![warn(clippy::std_instead_of_core)] #![allow(unused_imports)] extern crate alloc; +#[macro_use] +extern crate proc_macro_derive; + #[warn(clippy::std_instead_of_core)] fn std_instead_of_core() { // Regular import @@ -55,6 +59,13 @@ fn alloc_instead_of_core() { //~^ ERROR: used import from `alloc` instead of `core` } +mod std_in_proc_macro_derive { + #[warn(clippy::alloc_instead_of_core)] + #[allow(unused)] + #[derive(ImplStructWithStdDisplay)] + struct B {} +} + fn main() { std_instead_of_core(); std_instead_of_alloc(); diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs index 63a096384d7..af7f3399f49 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs @@ -1,8 +1,12 @@ +//@aux-build:proc_macro_derive.rs #![warn(clippy::std_instead_of_core)] #![allow(unused_imports)] extern crate alloc; +#[macro_use] +extern crate proc_macro_derive; + #[warn(clippy::std_instead_of_core)] fn std_instead_of_core() { // Regular import @@ -55,6 +59,13 @@ fn alloc_instead_of_core() { //~^ ERROR: used import from `alloc` instead of `core` } +mod std_in_proc_macro_derive { + #[warn(clippy::alloc_instead_of_core)] + #[allow(unused)] + #[derive(ImplStructWithStdDisplay)] + struct B {} +} + fn main() { std_instead_of_core(); std_instead_of_alloc(); diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr index ca26f77bd37..4f7bdc4045e 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr @@ -1,5 +1,5 @@ error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:9:9 + --> $DIR/std_instead_of_core.rs:13:9 | LL | use std::hash::Hasher; | ^^^ help: consider importing the item from `core`: `core` @@ -8,49 +8,49 @@ LL | use std::hash::Hasher; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:12:11 + --> $DIR/std_instead_of_core.rs:16:11 | LL | use ::std::hash::Hash; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:18:9 + --> $DIR/std_instead_of_core.rs:22:9 | LL | use std::fmt::{Debug, Result}; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:22:15 + --> $DIR/std_instead_of_core.rs:26:15 | LL | let ptr = std::ptr::null::<u32>(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:24:21 + --> $DIR/std_instead_of_core.rs:28:21 | LL | let ptr_mut = ::std::ptr::null_mut::<usize>(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:28:16 + --> $DIR/std_instead_of_core.rs:32:16 | LL | let cell = std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:30:27 + --> $DIR/std_instead_of_core.rs:34:27 | LL | let cell_absolute = ::std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:39:9 + --> $DIR/std_instead_of_core.rs:43:9 | LL | use std::iter::Iterator; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:46:9 + --> $DIR/std_instead_of_core.rs:50:9 | LL | use std::vec; | ^^^ help: consider importing the item from `alloc`: `alloc` @@ -59,13 +59,13 @@ LL | use std::vec; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:48:9 + --> $DIR/std_instead_of_core.rs:52:9 | LL | use std::vec::Vec; | ^^^ help: consider importing the item from `alloc`: `alloc` error: used import from `alloc` instead of `core` - --> $DIR/std_instead_of_core.rs:54:9 + --> $DIR/std_instead_of_core.rs:58:9 | LL | use alloc::slice::from_ref; | ^^^^^ help: consider importing the item from `core`: `core` diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed index 2828f9d048e..6fdd728b9b7 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed @@ -69,6 +69,34 @@ mod struct_mod { } } +// issue 9942 +mod underscore_mod { + // allow use of `deref` so that `clippy --fix` includes `Deref`. + #![allow(noop_method_call)] + + mod exports_underscore { + pub use std::ops::Deref as _; + pub fn dummy() {} + } + + mod exports_underscore_ish { + pub use std::ops::Deref as _Deref; + pub fn dummy() {} + } + + fn does_not_lint() { + use self::exports_underscore::*; + let _ = (&0).deref(); + dummy(); + } + + fn does_lint() { + use self::exports_underscore_ish::{_Deref, dummy}; + let _ = (&0).deref(); + dummy(); + } +} + fn main() { foo(); multi_foo(); diff --git a/src/tools/clippy/tests/ui/wildcard_imports.rs b/src/tools/clippy/tests/ui/wildcard_imports.rs index cbe70e505d8..20e06d4b366 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.rs +++ b/src/tools/clippy/tests/ui/wildcard_imports.rs @@ -69,6 +69,34 @@ mod struct_mod { } } +// issue 9942 +mod underscore_mod { + // allow use of `deref` so that `clippy --fix` includes `Deref`. + #![allow(noop_method_call)] + + mod exports_underscore { + pub use std::ops::Deref as _; + pub fn dummy() {} + } + + mod exports_underscore_ish { + pub use std::ops::Deref as _Deref; + pub fn dummy() {} + } + + fn does_not_lint() { + use self::exports_underscore::*; + let _ = (&0).deref(); + dummy(); + } + + fn does_lint() { + use self::exports_underscore_ish::*; + let _ = (&0).deref(); + dummy(); + } +} + fn main() { foo(); multi_foo(); diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr index 3c750815baf..01a5414778c 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr @@ -38,55 +38,61 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:97:13 + --> $DIR/wildcard_imports.rs:94:13 + | +LL | use self::exports_underscore_ish::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `self::exports_underscore_ish::{_Deref, dummy}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:125:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:103:75 + --> $DIR/wildcard_imports.rs:131:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:104:13 + --> $DIR/wildcard_imports.rs:132:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:116:20 + --> $DIR/wildcard_imports.rs:144:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:116:30 + --> $DIR/wildcard_imports.rs:144:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:123:13 + --> $DIR/wildcard_imports.rs:151:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:152:9 + --> $DIR/wildcard_imports.rs:180:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:161:9 + --> $DIR/wildcard_imports.rs:189:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:162:9 + --> $DIR/wildcard_imports.rs:190:9 | LL | use crate:: fn_mod:: | _________^ @@ -94,40 +100,40 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:173:13 + --> $DIR/wildcard_imports.rs:201:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:208:17 + --> $DIR/wildcard_imports.rs:236:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:216:13 + --> $DIR/wildcard_imports.rs:244:13 | LL | use crate::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:225:17 + --> $DIR/wildcard_imports.rs:253:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:234:13 + --> $DIR/wildcard_imports.rs:262:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:242:13 + --> $DIR/wildcard_imports.rs:270:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` -error: aborting due to 21 previous errors +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed index b27281fa25c..6a9fe007d65 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed @@ -64,6 +64,34 @@ mod struct_mod { } } +// issue 9942 +mod underscore_mod { + // allow use of `deref` so that `clippy --fix` includes `Deref`. + #![allow(noop_method_call)] + + mod exports_underscore { + pub use std::ops::Deref as _; + pub fn dummy() {} + } + + mod exports_underscore_ish { + pub use std::ops::Deref as _Deref; + pub fn dummy() {} + } + + fn does_not_lint() { + use exports_underscore::*; + let _ = (&0).deref(); + dummy(); + } + + fn does_lint() { + use exports_underscore_ish::{_Deref, dummy}; + let _ = (&0).deref(); + dummy(); + } +} + fn main() { foo(); multi_foo(); diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr index 709a665d65c..e39f240a4aa 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr @@ -38,55 +38,61 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:91:13 + --> $DIR/wildcard_imports_2021.rs:89:13 + | +LL | use exports_underscore_ish::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `exports_underscore_ish::{_Deref, dummy}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:119:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:97:75 + --> $DIR/wildcard_imports_2021.rs:125:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:98:13 + --> $DIR/wildcard_imports_2021.rs:126:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:110:20 + --> $DIR/wildcard_imports_2021.rs:138:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:110:30 + --> $DIR/wildcard_imports_2021.rs:138:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:117:13 + --> $DIR/wildcard_imports_2021.rs:145:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:146:9 + --> $DIR/wildcard_imports_2021.rs:174:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:155:9 + --> $DIR/wildcard_imports_2021.rs:183:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:156:9 + --> $DIR/wildcard_imports_2021.rs:184:9 | LL | use crate:: fn_mod:: | _________^ @@ -94,40 +100,40 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:167:13 + --> $DIR/wildcard_imports_2021.rs:195:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:202:17 + --> $DIR/wildcard_imports_2021.rs:230:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:210:13 + --> $DIR/wildcard_imports_2021.rs:238:13 | LL | use crate::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:219:17 + --> $DIR/wildcard_imports_2021.rs:247:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:228:13 + --> $DIR/wildcard_imports_2021.rs:256:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:236:13 + --> $DIR/wildcard_imports_2021.rs:264:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` -error: aborting due to 21 previous errors +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed index b27281fa25c..6a9fe007d65 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed @@ -64,6 +64,34 @@ mod struct_mod { } } +// issue 9942 +mod underscore_mod { + // allow use of `deref` so that `clippy --fix` includes `Deref`. + #![allow(noop_method_call)] + + mod exports_underscore { + pub use std::ops::Deref as _; + pub fn dummy() {} + } + + mod exports_underscore_ish { + pub use std::ops::Deref as _Deref; + pub fn dummy() {} + } + + fn does_not_lint() { + use exports_underscore::*; + let _ = (&0).deref(); + dummy(); + } + + fn does_lint() { + use exports_underscore_ish::{_Deref, dummy}; + let _ = (&0).deref(); + dummy(); + } +} + fn main() { foo(); multi_foo(); diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr index 709a665d65c..e39f240a4aa 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr @@ -38,55 +38,61 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:91:13 + --> $DIR/wildcard_imports_2021.rs:89:13 + | +LL | use exports_underscore_ish::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `exports_underscore_ish::{_Deref, dummy}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:119:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:97:75 + --> $DIR/wildcard_imports_2021.rs:125:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:98:13 + --> $DIR/wildcard_imports_2021.rs:126:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:110:20 + --> $DIR/wildcard_imports_2021.rs:138:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:110:30 + --> $DIR/wildcard_imports_2021.rs:138:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:117:13 + --> $DIR/wildcard_imports_2021.rs:145:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:146:9 + --> $DIR/wildcard_imports_2021.rs:174:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:155:9 + --> $DIR/wildcard_imports_2021.rs:183:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:156:9 + --> $DIR/wildcard_imports_2021.rs:184:9 | LL | use crate:: fn_mod:: | _________^ @@ -94,40 +100,40 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:167:13 + --> $DIR/wildcard_imports_2021.rs:195:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:202:17 + --> $DIR/wildcard_imports_2021.rs:230:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:210:13 + --> $DIR/wildcard_imports_2021.rs:238:13 | LL | use crate::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:219:17 + --> $DIR/wildcard_imports_2021.rs:247:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:228:13 + --> $DIR/wildcard_imports_2021.rs:256:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports_2021.rs:236:13 + --> $DIR/wildcard_imports_2021.rs:264:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` -error: aborting due to 21 previous errors +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.rs b/src/tools/clippy/tests/ui/wildcard_imports_2021.rs index 7dd2103eca7..18ebc0f5127 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.rs +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.rs @@ -64,6 +64,34 @@ mod struct_mod { } } +// issue 9942 +mod underscore_mod { + // allow use of `deref` so that `clippy --fix` includes `Deref`. + #![allow(noop_method_call)] + + mod exports_underscore { + pub use std::ops::Deref as _; + pub fn dummy() {} + } + + mod exports_underscore_ish { + pub use std::ops::Deref as _Deref; + pub fn dummy() {} + } + + fn does_not_lint() { + use exports_underscore::*; + let _ = (&0).deref(); + dummy(); + } + + fn does_lint() { + use exports_underscore_ish::*; + let _ = (&0).deref(); + dummy(); + } +} + fn main() { foo(); multi_foo(); diff --git a/src/tools/clippy/tests/ui/write_literal.fixed b/src/tools/clippy/tests/ui/write_literal.fixed index ee577574d28..3d216b76cbf 100644 --- a/src/tools/clippy/tests/ui/write_literal.fixed +++ b/src/tools/clippy/tests/ui/write_literal.fixed @@ -43,16 +43,22 @@ fn main() { // throw a warning writeln!(v, "hello world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string writeln!(v, "world hello"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // named args shouldn't change anything either writeln!(v, "hello world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string writeln!(v, "world hello"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string + + // #10128 + writeln!(v, "hello {0} world", 2); + //~^ ERROR: literal with an empty format string + writeln!(v, "world {0} hello", 2); + //~^ ERROR: literal with an empty format string + writeln!(v, "hello {0} {1}, {bar}", 2, 3, bar = 4); + //~^ ERROR: literal with an empty format string + writeln!(v, "hello {0} {1}, world {2}", 2, 3, 4); + //~^ ERROR: literal with an empty format string } diff --git a/src/tools/clippy/tests/ui/write_literal.rs b/src/tools/clippy/tests/ui/write_literal.rs index 588e8fd413a..79d6daa2e3b 100644 --- a/src/tools/clippy/tests/ui/write_literal.rs +++ b/src/tools/clippy/tests/ui/write_literal.rs @@ -43,16 +43,22 @@ fn main() { // throw a warning writeln!(v, "{0} {1}", "hello", "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string writeln!(v, "{1} {0}", "hello", "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // named args shouldn't change anything either writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string + + // #10128 + writeln!(v, "{0} {1} {2}", "hello", 2, "world"); + //~^ ERROR: literal with an empty format string + writeln!(v, "{2} {1} {0}", "hello", 2, "world"); + //~^ ERROR: literal with an empty format string + writeln!(v, "{0} {1} {2}, {bar}", "hello", 2, 3, bar = 4); + //~^ ERROR: literal with an empty format string + writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4); + //~^ ERROR: literal with an empty format string } diff --git a/src/tools/clippy/tests/ui/write_literal.stderr b/src/tools/clippy/tests/ui/write_literal.stderr index 372a54cf769..ee0d536e954 100644 --- a/src/tools/clippy/tests/ui/write_literal.stderr +++ b/src/tools/clippy/tests/ui/write_literal.stderr @@ -52,96 +52,96 @@ error: literal with an empty format string --> $DIR/write_literal.rs:44:28 | LL | writeln!(v, "{0} {1}", "hello", "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^ | help: try | LL - writeln!(v, "{0} {1}", "hello", "world"); -LL + writeln!(v, "hello {1}", "world"); +LL + writeln!(v, "hello world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:44:37 + --> $DIR/write_literal.rs:46:28 | -LL | writeln!(v, "{0} {1}", "hello", "world"); - | ^^^^^^^ +LL | writeln!(v, "{1} {0}", "hello", "world"); + | ^^^^^^^^^^^^^^^^ | help: try | -LL - writeln!(v, "{0} {1}", "hello", "world"); -LL + writeln!(v, "{0} world", "hello"); +LL - writeln!(v, "{1} {0}", "hello", "world"); +LL + writeln!(v, "world hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:47:37 + --> $DIR/write_literal.rs:50:38 | -LL | writeln!(v, "{1} {0}", "hello", "world"); - | ^^^^^^^ +LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^^^^^^^^^^^^^^^^ | help: try | -LL - writeln!(v, "{1} {0}", "hello", "world"); -LL + writeln!(v, "world {0}", "hello"); +LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); +LL + writeln!(v, "hello world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:47:28 + --> $DIR/write_literal.rs:52:38 | -LL | writeln!(v, "{1} {0}", "hello", "world"); - | ^^^^^^^ +LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^^^^^^^^^^^^^^^^ | help: try | -LL - writeln!(v, "{1} {0}", "hello", "world"); -LL + writeln!(v, "{1} hello", "world"); +LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); +LL + writeln!(v, "world hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:52:38 + --> $DIR/write_literal.rs:56:32 | -LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ +LL | writeln!(v, "{0} {1} {2}", "hello", 2, "world"); + | ^^^^^^^^^^^^^^^^^^^ | help: try | -LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); -LL + writeln!(v, "hello {bar}", bar = "world"); +LL - writeln!(v, "{0} {1} {2}", "hello", 2, "world"); +LL + writeln!(v, "hello {0} world", 2); | error: literal with an empty format string - --> $DIR/write_literal.rs:52:53 + --> $DIR/write_literal.rs:58:32 | -LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ +LL | writeln!(v, "{2} {1} {0}", "hello", 2, "world"); + | ^^^^^^^^^^^^^^^^^^^ | help: try | -LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); -LL + writeln!(v, "{foo} world", foo = "hello"); +LL - writeln!(v, "{2} {1} {0}", "hello", 2, "world"); +LL + writeln!(v, "world {0} hello", 2); | error: literal with an empty format string - --> $DIR/write_literal.rs:55:53 + --> $DIR/write_literal.rs:60:39 | -LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ +LL | writeln!(v, "{0} {1} {2}, {bar}", "hello", 2, 3, bar = 4); + | ^^^^^^^ | help: try | -LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); -LL + writeln!(v, "world {foo}", foo = "hello"); +LL - writeln!(v, "{0} {1} {2}, {bar}", "hello", 2, 3, bar = 4); +LL + writeln!(v, "hello {0} {1}, {bar}", 2, 3, bar = 4); | error: literal with an empty format string - --> $DIR/write_literal.rs:55:38 + --> $DIR/write_literal.rs:62:41 | -LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ +LL | writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4); + | ^^^^^^^^^^^^^^^^^^^^^^ | help: try | -LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); -LL + writeln!(v, "{bar} hello", bar = "world"); +LL - writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4); +LL + writeln!(v, "hello {0} {1}, world {2}", 2, 3, 4); | error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/write_literal_2.rs b/src/tools/clippy/tests/ui/write_literal_2.rs index aa0c13c1340..b2ed552d46b 100644 --- a/src/tools/clippy/tests/ui/write_literal_2.rs +++ b/src/tools/clippy/tests/ui/write_literal_2.rs @@ -1,6 +1,6 @@ //@no-rustfix: overlapping suggestions #![allow(unused_must_use)] -#![warn(clippy::needless_raw_strings, clippy::write_literal)] +#![warn(clippy::write_literal)] use std::io::Write; @@ -11,9 +11,7 @@ fn main() { //~^ ERROR: literal with an empty format string //~| NOTE: `-D clippy::write-literal` implied by `-D warnings` writeln!(v, r"{}", r"{hello}"); - //~^ ERROR: unnecessary raw string literal - //~| NOTE: `-D clippy::needless-raw-strings` implied by `-D warnings` - //~| ERROR: literal with an empty format string + //~^ ERROR: literal with an empty format string writeln!(v, "{}", '\''); //~^ ERROR: literal with an empty format string writeln!(v, "{}", '"'); @@ -26,17 +24,14 @@ fn main() { v, "some {}", "hello \ - //~^ ERROR: literal with an empty format string - world!" + world!", + //~^^ ERROR: literal with an empty format string ); writeln!( v, "some {}\ {} \\ {}", - "1", - "2", - "3", - //~^ ERROR: literal with an empty format string + "1", "2", "3", ); writeln!(v, "{}", "\\"); //~^ ERROR: literal with an empty format string @@ -51,7 +46,6 @@ fn main() { // hard mode writeln!(v, r#"{}{}"#, '#', '"'); //~^ ERROR: literal with an empty format string - //~| ERROR: literal with an empty format string // should not lint writeln!(v, r"{}", "\r"); } diff --git a/src/tools/clippy/tests/ui/write_literal_2.stderr b/src/tools/clippy/tests/ui/write_literal_2.stderr index 6d382a267ad..81ef49de082 100644 --- a/src/tools/clippy/tests/ui/write_literal_2.stderr +++ b/src/tools/clippy/tests/ui/write_literal_2.stderr @@ -1,14 +1,3 @@ -error: unnecessary raw string literal - --> $DIR/write_literal_2.rs:13:24 - | -LL | writeln!(v, r"{}", r"{hello}"); - | -^^^^^^^^^ - | | - | help: use a string literal instead: `"{hello}"` - | - = note: `-D clippy::needless-raw-strings` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]` - error: literal with an empty format string --> $DIR/write_literal_2.rs:10:23 | @@ -36,7 +25,7 @@ LL + writeln!(v, r"{{hello}}"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:17:23 + --> $DIR/write_literal_2.rs:15:23 | LL | writeln!(v, "{}", '\''); | ^^^^ @@ -48,7 +37,7 @@ LL + writeln!(v, "'"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:19:23 + --> $DIR/write_literal_2.rs:17:23 | LL | writeln!(v, "{}", '"'); | ^^^ @@ -60,13 +49,13 @@ LL + writeln!(v, "\""); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:21:24 + --> $DIR/write_literal_2.rs:19:24 | LL | writeln!(v, r"{}", '"'); | ^^^ error: literal with an empty format string - --> $DIR/write_literal_2.rs:23:24 + --> $DIR/write_literal_2.rs:21:24 | LL | writeln!(v, r"{}", '\''); | ^^^^ @@ -78,59 +67,32 @@ LL + writeln!(v, r"'"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:28:9 + --> $DIR/write_literal_2.rs:26:9 | LL | / "hello \ -LL | | -LL | | world!" +LL | | world!", | |_______________^ | help: try | LL ~ "some hello \ -LL + -LL ~ world!" +LL ~ world!", | error: literal with an empty format string - --> $DIR/write_literal_2.rs:36:9 + --> $DIR/write_literal_2.rs:34:9 | -LL | "1", - | ^^^ +LL | "1", "2", "3", + | ^^^^^^^^^^^^^ | help: try | LL ~ "some 1\ -LL ~ {} \\ {}", +LL ~ 2 \\ 3", | error: literal with an empty format string - --> $DIR/write_literal_2.rs:37:9 - | -LL | "2", - | ^^^ - | -help: try - | -LL ~ 2 \\ {}", -LL ~ "1", - | - -error: literal with an empty format string - --> $DIR/write_literal_2.rs:38:9 - | -LL | "3", - | ^^^ - | -help: try - | -LL ~ {} \\ 3", -LL | "1", -LL ~ "2", - | - -error: literal with an empty format string - --> $DIR/write_literal_2.rs:41:23 + --> $DIR/write_literal_2.rs:36:23 | LL | writeln!(v, "{}", "\\"); | ^^^^ @@ -142,7 +104,7 @@ LL + writeln!(v, "\\"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:43:24 + --> $DIR/write_literal_2.rs:38:24 | LL | writeln!(v, r"{}", "\\"); | ^^^^ @@ -154,7 +116,7 @@ LL + writeln!(v, r"\"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:45:26 + --> $DIR/write_literal_2.rs:40:26 | LL | writeln!(v, r#"{}"#, "\\"); | ^^^^ @@ -166,7 +128,7 @@ LL + writeln!(v, r#"\"#); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:47:23 + --> $DIR/write_literal_2.rs:42:23 | LL | writeln!(v, "{}", r"\"); | ^^^^ @@ -178,7 +140,7 @@ LL + writeln!(v, "\\"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:49:23 + --> $DIR/write_literal_2.rs:44:23 | LL | writeln!(v, "{}", "\r"); | ^^^^ @@ -190,16 +152,10 @@ LL + writeln!(v, "\r"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:52:28 - | -LL | writeln!(v, r#"{}{}"#, '#', '"'); - | ^^^ - -error: literal with an empty format string - --> $DIR/write_literal_2.rs:52:33 + --> $DIR/write_literal_2.rs:47:28 | LL | writeln!(v, r#"{}{}"#, '#', '"'); - | ^^^ + | ^^^^^^^^ -error: aborting due to 18 previous errors +error: aborting due to 14 previous errors diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index dc2601e9e9f..8f0a0a045ab 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -1bc0463b183392ad4e0ae9c5f7a76630d487230d +4ea5190026dbc1302b644d938e68bc6843cb8b24 diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index ef4e7df3aba..9041683fbc4 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -803,8 +803,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if tcx.is_foreign_item(def_id) { throw_unsup_format!("foreign thread-local statics are not supported"); } - // We don't give a span -- statics don't need that, they cannot be generic or associated. - let allocation = this.ctfe_query(None, |tcx| tcx.eval_static_initializer(def_id))?; + let allocation = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?; let mut allocation = allocation.inner().clone(); // This allocation will be deallocated when the thread dies, so it is not in read-only memory. allocation.mutability = Mutability::Mut; diff --git a/tests/run-make/silly-file-names/Makefile b/tests/run-make/silly-file-names/Makefile new file mode 100644 index 00000000000..a09bdb1c532 --- /dev/null +++ b/tests/run-make/silly-file-names/Makefile @@ -0,0 +1,18 @@ +# ignore-cross-compile we need to execute the binary +# ignore-windows we create files with < and > in their names + +include ../tools.mk + +ifdef RUSTC_BLESS_TEST + RUSTC_TEST_OP = cp +else + RUSTC_TEST_OP = $(DIFF) +endif + +all: + echo '"comes from a file with a name that begins with <"' > "$(TMPDIR)/<leading-lt" + echo '"comes from a file with a name that ends with >"' > "$(TMPDIR)/trailing-gt>" + cp silly-file-names.rs "$(TMPDIR)/silly-file-names.rs" + $(RUSTC) "$(TMPDIR)/silly-file-names.rs" -o "$(TMPDIR)/silly-file-names" + "$(TMPDIR)/silly-file-names" > "$(TMPDIR)/silly-file-names.run.stdout" + $(RUSTC_TEST_OP) "$(TMPDIR)/silly-file-names.run.stdout" silly-file-names.run.stdout diff --git a/tests/run-make/silly-file-names/silly-file-names.rs b/tests/run-make/silly-file-names/silly-file-names.rs new file mode 100644 index 00000000000..a2008209141 --- /dev/null +++ b/tests/run-make/silly-file-names/silly-file-names.rs @@ -0,0 +1,4 @@ +fn main() { + println!(include!("<leading-lt")); + println!(include!("trailing-gt>")); +} diff --git a/tests/run-make/silly-file-names/silly-file-names.run.stdout b/tests/run-make/silly-file-names/silly-file-names.run.stdout new file mode 100644 index 00000000000..fbc4c89e0f0 --- /dev/null +++ b/tests/run-make/silly-file-names/silly-file-names.run.stdout @@ -0,0 +1,2 @@ +comes from a file with a name that begins with < +comes from a file with a name that ends with > diff --git a/tests/ui/array-slice-vec/vector-no-ann.stderr b/tests/ui/array-slice-vec/vector-no-ann.stderr index d2ea08aa474..619417a73c9 100644 --- a/tests/ui/array-slice-vec/vector-no-ann.stderr +++ b/tests/ui/array-slice-vec/vector-no-ann.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed for `Vec<T>` --> $DIR/vector-no-ann.rs:2:9 | LL | let _foo = Vec::new(); - | ^^^^ + | ^^^^ ---------- type must be known at this point | help: consider giving `_foo` an explicit type, where the type for type parameter `T` is specified | diff --git a/tests/ui/asm/x86_64/issue-96797.rs b/tests/ui/asm/x86_64/issue-96797.rs index 951dd949b32..6c22c2f6c94 100644 --- a/tests/ui/asm/x86_64/issue-96797.rs +++ b/tests/ui/asm/x86_64/issue-96797.rs @@ -11,7 +11,14 @@ use std::arch::global_asm; #[no_mangle] fn my_func() {} -global_asm!("call_foobar: jmp {}", sym foobar); +global_asm!(" +.globl call_foobar +.type call_foobar,@function +.pushsection .text.call_foobar,\"ax\",@progbits +call_foobar: jmp {} +.size call_foobar, .-call_foobar +.popsection +", sym foobar); fn foobar() {} diff --git a/tests/ui/const-generics/const-argument-if-length.full.stderr b/tests/ui/const-generics/const-argument-if-length.full.stderr index 7997026dfe4..315b0f0a064 100644 --- a/tests/ui/const-generics/const-argument-if-length.full.stderr +++ b/tests/ui/const-generics/const-argument-if-length.full.stderr @@ -1,3 +1,11 @@ +error: unconstrained generic constant + --> $DIR/const-argument-if-length.rs:17:10 + | +LL | pad: [u8; is_zst::<T>()], + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); is_zst::<T>()]:` + error[E0277]: the size for values of type `T` cannot be known at compilation time --> $DIR/const-argument-if-length.rs:15:12 | @@ -22,14 +30,6 @@ help: the `Box` type always has a statically known size and allocates its conten LL | value: Box<T>, | ++++ + -error: unconstrained generic constant - --> $DIR/const-argument-if-length.rs:17:10 - | -LL | pad: [u8; is_zst::<T>()], - | ^^^^^^^^^^^^^^^^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); is_zst::<T>()]:` - error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/issues/issue-83249.stderr b/tests/ui/const-generics/issues/issue-83249.stderr index 7491fdc8a69..5187434ff40 100644 --- a/tests/ui/const-generics/issues/issue-83249.stderr +++ b/tests/ui/const-generics/issues/issue-83249.stderr @@ -1,9 +1,18 @@ -error[E0282]: type annotations needed +error[E0283]: type annotations needed --> $DIR/issue-83249.rs:19:9 | LL | let _ = foo([0; 1]); - | ^ + | ^ --- ------ type must be known at this point + | | + | required by a bound introduced by this call | + = note: cannot satisfy `_: Foo` + = help: the trait `Foo` is implemented for `u8` +note: required by a bound in `foo` + --> $DIR/issue-83249.rs:12:11 + | +LL | fn foo<T: Foo>(_: [u8; T::N]) -> T { + | ^^^ required by this bound in `foo` help: consider giving this pattern a type | LL | let _: /* Type */ = foo([0; 1]); @@ -11,4 +20,4 @@ LL | let _: /* Type */ = foo([0; 1]); error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/error-codes/E0282.rs b/tests/ui/error-codes/E0282.rs index f1f93b3aed6..5bc29cda9ea 100644 --- a/tests/ui/error-codes/E0282.rs +++ b/tests/ui/error-codes/E0282.rs @@ -1,4 +1,4 @@ fn main() { - let x = "hello".chars().rev().collect(); + let x; //~^ ERROR E0282 } diff --git a/tests/ui/error-codes/E0282.stderr b/tests/ui/error-codes/E0282.stderr index 892d3a81f27..58332454a37 100644 --- a/tests/ui/error-codes/E0282.stderr +++ b/tests/ui/error-codes/E0282.stderr @@ -1,13 +1,13 @@ error[E0282]: type annotations needed --> $DIR/E0282.rs:2:9 | -LL | let x = "hello".chars().rev().collect(); +LL | let x; | ^ | help: consider giving `x` an explicit type | -LL | let x: Vec<_> = "hello".chars().rev().collect(); - | ++++++++ +LL | let x: /* Type */; + | ++++++++++++ error: aborting due to previous error diff --git a/tests/ui/error-codes/E0401.stderr b/tests/ui/error-codes/E0401.stderr index 928c8d11d20..0a069e8d350 100644 --- a/tests/ui/error-codes/E0401.stderr +++ b/tests/ui/error-codes/E0401.stderr @@ -32,12 +32,18 @@ LL | fn helper(sel: &Self) -> u8 { | use of generic parameter from outer item | refer to the type directly here instead -error[E0282]: type annotations needed +error[E0283]: type annotations needed --> $DIR/E0401.rs:11:5 | LL | bfnr(x); - | ^^^^ cannot infer type of the type parameter `U` declared on the function `bfnr` + | ^^^^ cannot infer type of the type parameter `V` declared on the function `bfnr` + | + = note: cannot satisfy `_: Baz<_>` +note: required by a bound in `bfnr` + --> $DIR/E0401.rs:4:19 | +LL | fn bfnr<U, V: Baz<U>, W: Fn()>(y: T) { + | ^^^^^^ required by this bound in `bfnr` help: consider specifying the generic arguments | LL | bfnr::<U, V, W>(x); @@ -66,5 +72,5 @@ LL | bfnr::<U, V, W>(x); error: aborting due to 5 previous errors -Some errors have detailed explanations: E0282, E0283, E0401. -For more information about an error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0283, E0401. +For more information about an error, try `rustc --explain E0283`. diff --git a/tests/ui/for/issue-20605.next.stderr b/tests/ui/for/issue-20605.next.stderr index 0955efdbb73..28dbdf4c374 100644 --- a/tests/ui/for/issue-20605.next.stderr +++ b/tests/ui/for/issue-20605.next.stderr @@ -44,12 +44,6 @@ LL | for item in *things { *item = 0 } = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature -error: the type `Option<<<dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter as Iterator>::Item>` is not well-formed - --> $DIR/issue-20605.rs:5:17 - | -LL | for item in *things { *item = 0 } - | ^^^^^^^ - error[E0277]: the size for values of type `<<dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter as Iterator>::Item` cannot be known at compilation time --> $DIR/issue-20605.rs:5:5 | @@ -60,6 +54,12 @@ LL | for item in *things { *item = 0 } note: required by a bound in `None` --> $SRC_DIR/core/src/option.rs:LL:COL +error: the type `Option<<<dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter as Iterator>::Item>` is not well-formed + --> $DIR/issue-20605.rs:5:17 + | +LL | for item in *things { *item = 0 } + | ^^^^^^^ + error[E0614]: type `<<dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter as Iterator>::Item` cannot be dereferenced --> $DIR/issue-20605.rs:5:27 | diff --git a/tests/ui/generic-associated-types/bugs/issue-91762.stderr b/tests/ui/generic-associated-types/bugs/issue-91762.stderr index 1272c8b8ae2..1045e80f046 100644 --- a/tests/ui/generic-associated-types/bugs/issue-91762.stderr +++ b/tests/ui/generic-associated-types/bugs/issue-91762.stderr @@ -1,9 +1,10 @@ -error[E0282]: type annotations needed +error[E0284]: type annotations needed --> $DIR/issue-91762.rs:24:15 | LL | ret = <Self::Base as Functor>::fmap(arg); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the associated function `fmap` | + = note: cannot satisfy `<<Self as FunctorExt<T>>::Base as Functor>::With<_> == Self` help: consider specifying the generic arguments | LL | ret = <Self::Base as Functor>::fmap::<T, U>(arg); @@ -11,4 +12,4 @@ LL | ret = <Self::Base as Functor>::fmap::<T, U>(arg); error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/generic-const-items/inference-failure.stderr b/tests/ui/generic-const-items/inference-failure.stderr index 22ff1b9ba7f..10ecd83ec53 100644 --- a/tests/ui/generic-const-items/inference-failure.stderr +++ b/tests/ui/generic-const-items/inference-failure.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed for `Option<T>` --> $DIR/inference-failure.rs:8:9 | LL | let _ = NONE; - | ^ + | ^ ---- type must be known at this point | help: consider giving this pattern a type, where the type for type parameter `T` is specified | diff --git a/tests/ui/impl-trait/cross-return-site-inference.rs b/tests/ui/impl-trait/cross-return-site-inference.rs index 00aed2ad95a..e1071b08c55 100644 --- a/tests/ui/impl-trait/cross-return-site-inference.rs +++ b/tests/ui/impl-trait/cross-return-site-inference.rs @@ -36,13 +36,13 @@ fn muh() -> Result<(), impl std::fmt::Debug> { fn muh2() -> Result<(), impl std::fmt::Debug> { return Err(From::from("foo")); - //~^ ERROR type annotations needed + //~^ ERROR cannot call associated function on trait Ok(()) } fn muh3() -> Result<(), impl std::fmt::Debug> { Err(From::from("foo")) - //~^ ERROR type annotations needed + //~^ ERROR cannot call associated function on trait } fn main() {} diff --git a/tests/ui/impl-trait/cross-return-site-inference.stderr b/tests/ui/impl-trait/cross-return-site-inference.stderr index 766614e9e50..8ee3e9abf9c 100644 --- a/tests/ui/impl-trait/cross-return-site-inference.stderr +++ b/tests/ui/impl-trait/cross-return-site-inference.stderr @@ -9,28 +9,29 @@ help: consider specifying the generic arguments LL | Ok::<(), E>(()) | +++++++++ -error[E0282]: type annotations needed - --> $DIR/cross-return-site-inference.rs:38:12 +error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type + --> $DIR/cross-return-site-inference.rs:38:16 | LL | return Err(From::from("foo")); - | ^^^ cannot infer type of the type parameter `E` declared on the enum `Result` + | ^^^^^^^^^^ cannot call associated function of trait | -help: consider specifying the generic arguments +help: use a fully-qualified path to a specific available implementation | -LL | return Err::<(), E>(From::from("foo")); - | +++++++++ +LL | return Err(</* self type */ as From>::from("foo")); + | +++++++++++++++++++ + -error[E0282]: type annotations needed - --> $DIR/cross-return-site-inference.rs:44:5 +error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type + --> $DIR/cross-return-site-inference.rs:44:9 | LL | Err(From::from("foo")) - | ^^^ cannot infer type of the type parameter `E` declared on the enum `Result` + | ^^^^^^^^^^ cannot call associated function of trait | -help: consider specifying the generic arguments +help: use a fully-qualified path to a specific available implementation | -LL | Err::<(), E>(From::from("foo")) - | +++++++++ +LL | Err(</* self type */ as From>::from("foo")) + | +++++++++++++++++++ + error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0282, E0790. +For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/issues/issue-84073.stderr b/tests/ui/impl-trait/issues/issue-84073.stderr index 3c39aa6ce5b..b4be16ff042 100644 --- a/tests/ui/impl-trait/issues/issue-84073.stderr +++ b/tests/ui/impl-trait/issues/issue-84073.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed for `RaceBuilder<T, Never<T>>` --> $DIR/issue-84073.rs:32:16 | LL | Race::new(|race| race.when()); - | ^^^^ + | ^^^^ ---- type must be known at this point | help: consider giving this closure parameter an explicit type, where the type for type parameter `T` is specified | diff --git a/tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr b/tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr index ee4343b110f..2347805343c 100644 --- a/tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr +++ b/tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr @@ -1,9 +1,11 @@ -error[E0282]: type annotations needed +error[E0283]: type annotations needed --> $DIR/opaque-cast-field-access-in-future.rs:22:17 | LL | fn run() -> Foo<impl Future<Output = ()>> { | ^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + | + = note: cannot satisfy `_: Future` error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/impl-trait/where-allowed-2.stderr b/tests/ui/impl-trait/where-allowed-2.stderr index 2b328c01c87..b3765ac1a54 100644 --- a/tests/ui/impl-trait/where-allowed-2.stderr +++ b/tests/ui/impl-trait/where-allowed-2.stderr @@ -1,9 +1,11 @@ -error[E0282]: type annotations needed +error[E0283]: type annotations needed --> $DIR/where-allowed-2.rs:3:30 | LL | fn in_adt_in_return() -> Vec<impl Debug> { panic!() } | ^^^^^^^^^^ cannot infer type + | + = note: cannot satisfy `_: Debug` error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/inference/cannot-infer-closure-circular.stderr b/tests/ui/inference/cannot-infer-closure-circular.stderr index b706cd2bc36..98639f307fb 100644 --- a/tests/ui/inference/cannot-infer-closure-circular.stderr +++ b/tests/ui/inference/cannot-infer-closure-circular.stderr @@ -3,6 +3,9 @@ error[E0282]: type annotations needed for `Result<(), E>` | LL | let x = |r| { | ^ +LL | let v = r?; +LL | Ok(v) + | ----- type must be known at this point | help: consider giving this closure parameter an explicit type, where the type for type parameter `E` is specified | diff --git a/tests/ui/inference/issue-104649.stderr b/tests/ui/inference/issue-104649.stderr index 4962b21f9fd..2819329275d 100644 --- a/tests/ui/inference/issue-104649.stderr +++ b/tests/ui/inference/issue-104649.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed for `A<std::result::Result<std::result::Re --> $DIR/issue-104649.rs:24:9 | LL | let a = A(Result::Ok(Result::Ok(()))); - | ^ + | ^ -------------- type must be known at this point | help: consider giving `a` an explicit type, where the type for type parameter `E` is specified | diff --git a/tests/ui/inference/issue-71584.rs b/tests/ui/inference/issue-71584.rs index 7bf3ed60ec1..c96c32d5c0a 100644 --- a/tests/ui/inference/issue-71584.rs +++ b/tests/ui/inference/issue-71584.rs @@ -1,3 +1,4 @@ +// ignore-windows different list of satisfying impls fn main() { let n: u32 = 1; let mut d: u64 = 2; diff --git a/tests/ui/inference/issue-71584.stderr b/tests/ui/inference/issue-71584.stderr index 6ddb7657301..22c0f113d6a 100644 --- a/tests/ui/inference/issue-71584.stderr +++ b/tests/ui/inference/issue-71584.stderr @@ -1,5 +1,5 @@ error[E0284]: type annotations needed - --> $DIR/issue-71584.rs:4:15 + --> $DIR/issue-71584.rs:5:15 | LL | d = d % n.into(); | - ^^^^ diff --git a/tests/ui/inference/issue-72690.stderr b/tests/ui/inference/issue-72690.stderr index 225558805e0..6c93241ea07 100644 --- a/tests/ui/inference/issue-72690.stderr +++ b/tests/ui/inference/issue-72690.stderr @@ -24,16 +24,15 @@ help: try using a fully qualified path to specify the expected types LL | String::from(<str as AsRef<T>>::as_ref("x")); | ++++++++++++++++++++++++++ ~ -error[E0282]: type annotations needed - --> $DIR/issue-72690.rs:12:6 +error[E0283]: type annotations needed + --> $DIR/issue-72690.rs:12:9 | LL | |x| String::from("x".as_ref()); - | ^ - | -help: consider giving this closure parameter an explicit type + | ^^^^^^ cannot infer type for reference `&_` | -LL | |x: /* Type */| String::from("x".as_ref()); - | ++++++++++++ + = note: multiple `impl`s satisfying `String: From<&_>` found in the `alloc` crate: + - impl From<&String> for String; + - impl From<&str> for String; error[E0283]: type annotations needed --> $DIR/issue-72690.rs:12:26 @@ -225,5 +224,4 @@ LL | String::from(<str as AsRef<T>>::as_ref("x")); error: aborting due to 17 previous errors -Some errors have detailed explanations: E0282, E0283. -For more information about an error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/inference/multiple-impl-apply.rs b/tests/ui/inference/multiple-impl-apply.rs new file mode 100644 index 00000000000..314fe0f2ae5 --- /dev/null +++ b/tests/ui/inference/multiple-impl-apply.rs @@ -0,0 +1,48 @@ +struct Foo { + inner: u32, +} + +struct Bar { + inner: u32, +} + +#[derive(Clone, Copy)] +struct Baz { + inner: u32, +} + +impl From<Baz> for Bar { + fn from(other: Baz) -> Self { + Self { + inner: other.inner, + } + } +} + +impl From<Baz> for Foo { + fn from(other: Baz) -> Self { + Self { + inner: other.inner, + } + } +} + +fn main() { + let x: Baz = Baz { inner: 42 }; + + // DOESN'T Compile: Multiple options! + let y = x.into(); //~ ERROR E0283 + + let y_1: Foo = x.into(); + let y_2: Bar = x.into(); + + let z_1 = Foo::from(y_1); + let z_2 = Bar::from(y_2); + + // No type annotations needed, the compiler KNOWS the type must be `Foo`! + let m = magic_foo(x); +} + +fn magic_foo(arg: Baz) -> Foo { + arg.into() +} diff --git a/tests/ui/inference/multiple-impl-apply.stderr b/tests/ui/inference/multiple-impl-apply.stderr new file mode 100644 index 00000000000..ec49e15201a --- /dev/null +++ b/tests/ui/inference/multiple-impl-apply.stderr @@ -0,0 +1,23 @@ +error[E0283]: type annotations needed + --> $DIR/multiple-impl-apply.rs:34:9 + | +LL | let y = x.into(); + | ^ ---- type must be known at this point + | +note: multiple `impl`s satisfying `_: From<Baz>` found + --> $DIR/multiple-impl-apply.rs:14:1 + | +LL | impl From<Baz> for Bar { + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | impl From<Baz> for Foo { + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: required for `Baz` to implement `Into<_>` +help: consider giving `y` an explicit type + | +LL | let y: /* Type */ = x.into(); + | ++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/inference/need_type_info/concrete-impl.rs b/tests/ui/inference/need_type_info/concrete-impl.rs index fc79e6201bd..8960f870c39 100644 --- a/tests/ui/inference/need_type_info/concrete-impl.rs +++ b/tests/ui/inference/need_type_info/concrete-impl.rs @@ -14,6 +14,4 @@ fn main() { <Struct as Ambiguous<_>>::method(); //~^ ERROR type annotations needed //~| NOTE cannot infer type of the type parameter `A` - //~| ERROR type annotations needed - //~| NOTE infer type of the type parameter `A` } diff --git a/tests/ui/inference/need_type_info/concrete-impl.stderr b/tests/ui/inference/need_type_info/concrete-impl.stderr index 74c3f6cd5cf..6b86753caf9 100644 --- a/tests/ui/inference/need_type_info/concrete-impl.stderr +++ b/tests/ui/inference/need_type_info/concrete-impl.stderr @@ -1,9 +1,3 @@ -error[E0282]: type annotations needed - --> $DIR/concrete-impl.rs:14:5 - | -LL | <Struct as Ambiguous<_>>::method(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `A` declared on the trait `Ambiguous` - error[E0283]: type annotations needed --> $DIR/concrete-impl.rs:14:5 | @@ -19,7 +13,6 @@ LL | LL | impl Ambiguous<Two> for Struct {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0282, E0283. -For more information about an error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/inference/need_type_info/issue-113264-incorrect-impl-trait-in-path-suggestion.stderr b/tests/ui/inference/need_type_info/issue-113264-incorrect-impl-trait-in-path-suggestion.stderr index 0ec219415ab..9ca94cd58df 100644 --- a/tests/ui/inference/need_type_info/issue-113264-incorrect-impl-trait-in-path-suggestion.stderr +++ b/tests/ui/inference/need_type_info/issue-113264-incorrect-impl-trait-in-path-suggestion.stderr @@ -1,9 +1,17 @@ -error[E0282]: type annotations needed +error[E0283]: type annotations needed --> $DIR/issue-113264-incorrect-impl-trait-in-path-suggestion.rs:10:16 | LL | (S {}).owo(None) - | ^^^^ cannot infer type of the type parameter `T` declared on the enum `Option` + | --- ^^^^ cannot infer type of the type parameter `T` declared on the enum `Option` + | | + | required by a bound introduced by this call | + = note: cannot satisfy `_: T` +note: required by a bound in `S::owo` + --> $DIR/issue-113264-incorrect-impl-trait-in-path-suggestion.rs:6:35 + | +LL | fn owo(&self, _: Option<&impl T>) {} + | ^ required by this bound in `S::owo` help: consider specifying the generic argument | LL | (S {}).owo(None::<&_>) @@ -11,4 +19,4 @@ LL | (S {}).owo(None::<&_>) error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/inference/question-mark-type-infer.stderr b/tests/ui/inference/question-mark-type-infer.stderr index 7a1e850d157..52baa213302 100644 --- a/tests/ui/inference/question-mark-type-infer.stderr +++ b/tests/ui/inference/question-mark-type-infer.stderr @@ -1,9 +1,12 @@ -error[E0282]: type annotations needed +error[E0283]: type annotations needed --> $DIR/question-mark-type-infer.rs:10:21 | LL | l.iter().map(f).collect()? | ^^^^^^^ cannot infer type of the type parameter `B` declared on the method `collect` | + = note: cannot satisfy `_: FromIterator<Result<i32, ()>>` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL help: consider specifying the generic argument | LL | l.iter().map(f).collect::<Vec<_>>()? @@ -11,4 +14,4 @@ LL | l.iter().map(f).collect::<Vec<_>>()? error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/issues/issue-12187-1.stderr b/tests/ui/issues/issue-12187-1.stderr index 806b7f0ac05..d2a6a96082e 100644 --- a/tests/ui/issues/issue-12187-1.stderr +++ b/tests/ui/issues/issue-12187-1.stderr @@ -2,9 +2,9 @@ error[E0282]: type annotations needed for `&T` --> $DIR/issue-12187-1.rs:6:9 | LL | let &v = new(); - | ^^ + | ^^ ----- type must be known at this point | -help: consider giving this pattern a type, where the placeholders `_` are specified +help: consider giving this pattern a type, where the type for type parameter `T` is specified | LL | let &v: &T = new(); | ++++ diff --git a/tests/ui/issues/issue-12187-2.stderr b/tests/ui/issues/issue-12187-2.stderr index a1fa0a2b002..ac75ebb4256 100644 --- a/tests/ui/issues/issue-12187-2.stderr +++ b/tests/ui/issues/issue-12187-2.stderr @@ -2,9 +2,9 @@ error[E0282]: type annotations needed for `&T` --> $DIR/issue-12187-2.rs:6:9 | LL | let &v = new(); - | ^^ + | ^^ ----- type must be known at this point | -help: consider giving this pattern a type, where the placeholders `_` are specified +help: consider giving this pattern a type, where the type for type parameter `T` is specified | LL | let &v: &T = new(); | ++++ diff --git a/tests/ui/issues/issue-16966.stderr b/tests/ui/issues/issue-16966.stderr index 8c92505b5eb..c53707c5d69 100644 --- a/tests/ui/issues/issue-16966.stderr +++ b/tests/ui/issues/issue-16966.stderr @@ -1,9 +1,16 @@ -error[E0282]: type annotations needed +error[E0283]: type annotations needed --> $DIR/issue-16966.rs:2:12 | LL | panic!(std::default::Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + | -------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | | + | | cannot infer type + | required by a bound introduced by this call + | + = note: cannot satisfy `_: Any` +note: required by a bound in `begin_panic` + --> $SRC_DIR/std/src/panicking.rs:LL:COL error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/issues/issue-17551.stderr b/tests/ui/issues/issue-17551.stderr index 5f45a2f8443..3a8f569a3a5 100644 --- a/tests/ui/issues/issue-17551.stderr +++ b/tests/ui/issues/issue-17551.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed for `B<T>` --> $DIR/issue-17551.rs:6:9 | LL | let foo = B(marker::PhantomData); - | ^^^ + | ^^^ ------------------- type must be known at this point | help: consider giving `foo` an explicit type, where the type for type parameter `T` is specified | diff --git a/tests/ui/issues/issue-24036.stderr b/tests/ui/issues/issue-24036.stderr index a425816cdeb..184383b7369 100644 --- a/tests/ui/issues/issue-24036.stderr +++ b/tests/ui/issues/issue-24036.stderr @@ -11,12 +11,13 @@ LL | x = |c| c + 1; = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object -error[E0282]: type annotations needed +error[E0284]: type annotations needed --> $DIR/issue-24036.rs:9:15 | LL | 1 => |c| c + 1, - | ^ + | ^ - type must be known at this point | + = note: cannot satisfy `<_ as Add<i32>>::Output == _` help: consider giving this closure parameter an explicit type | LL | 1 => |c: /* Type */| c + 1, @@ -24,5 +25,5 @@ LL | 1 => |c: /* Type */| c + 1, error: aborting due to 2 previous errors -Some errors have detailed explanations: E0282, E0308. -For more information about an error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0284, E0308. +For more information about an error, try `rustc --explain E0284`. diff --git a/tests/ui/issues/issue-24446.stderr b/tests/ui/issues/issue-24446.stderr index 4afb87c4825..72d528f1619 100644 --- a/tests/ui/issues/issue-24446.stderr +++ b/tests/ui/issues/issue-24446.stderr @@ -1,19 +1,19 @@ -error[E0277]: the size for values of type `(dyn Fn() -> u32 + 'static)` cannot be known at compilation time +error[E0277]: `(dyn Fn() -> u32 + 'static)` cannot be shared between threads safely --> $DIR/issue-24446.rs:2:17 | LL | static foo: dyn Fn() -> u32 = || -> u32 { - | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | ^^^^^^^^^^^^^^^ `(dyn Fn() -> u32 + 'static)` cannot be shared between threads safely | - = help: the trait `Sized` is not implemented for `(dyn Fn() -> u32 + 'static)` + = help: the trait `Sync` is not implemented for `(dyn Fn() -> u32 + 'static)` + = note: shared static variables must have a type that implements `Sync` -error[E0277]: `(dyn Fn() -> u32 + 'static)` cannot be shared between threads safely +error[E0277]: the size for values of type `(dyn Fn() -> u32 + 'static)` cannot be known at compilation time --> $DIR/issue-24446.rs:2:17 | LL | static foo: dyn Fn() -> u32 = || -> u32 { - | ^^^^^^^^^^^^^^^ `(dyn Fn() -> u32 + 'static)` cannot be shared between threads safely + | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `Sync` is not implemented for `(dyn Fn() -> u32 + 'static)` - = note: shared static variables must have a type that implements `Sync` + = help: the trait `Sized` is not implemented for `(dyn Fn() -> u32 + 'static)` error: aborting due to 2 previous errors diff --git a/tests/ui/iterators/collect-into-slice.stderr b/tests/ui/iterators/collect-into-slice.stderr index 07dc561f06a..45685ef0ce9 100644 --- a/tests/ui/iterators/collect-into-slice.stderr +++ b/tests/ui/iterators/collect-into-slice.stderr @@ -1,3 +1,13 @@ +error[E0277]: a slice of type `[i32]` cannot be built since `[i32]` has no definite size + --> $DIR/collect-into-slice.rs:6:38 + | +LL | let some_generated_vec = (0..10).collect(); + | ^^^^^^^ try explicitly collecting into a `Vec<{integer}>` + | + = help: the trait `FromIterator<{integer}>` is not implemented for `[i32]` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + error[E0277]: the size for values of type `[i32]` cannot be known at compilation time --> $DIR/collect-into-slice.rs:6:9 | @@ -18,16 +28,6 @@ LL | let some_generated_vec = (0..10).collect(); note: required by a bound in `collect` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error[E0277]: a slice of type `[i32]` cannot be built since `[i32]` has no definite size - --> $DIR/collect-into-slice.rs:6:38 - | -LL | let some_generated_vec = (0..10).collect(); - | ^^^^^^^ try explicitly collecting into a `Vec<{integer}>` - | - = help: the trait `FromIterator<{integer}>` is not implemented for `[i32]` -note: required by a bound in `collect` - --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - error[E0277]: a slice of type `&[i32]` cannot be built since we need to store the elements somewhere --> $DIR/collect-into-slice.rs:18:38 | diff --git a/tests/ui/methods/method-ambig-one-trait-unknown-int-type.stderr b/tests/ui/methods/method-ambig-one-trait-unknown-int-type.stderr index e0f8a5447b0..0a022dc3984 100644 --- a/tests/ui/methods/method-ambig-one-trait-unknown-int-type.stderr +++ b/tests/ui/methods/method-ambig-one-trait-unknown-int-type.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed for `Vec<T>` --> $DIR/method-ambig-one-trait-unknown-int-type.rs:24:9 | LL | let mut x = Vec::new(); - | ^^^^^ + | ^^^^^ ---------- type must be known at this point | help: consider giving `x` an explicit type, where the type for type parameter `T` is specified | diff --git a/tests/ui/parser/assoc-const-underscore-semantic-fail.rs b/tests/ui/parser/assoc/assoc-const-underscore-semantic-fail.rs index d37ce06c555..d37ce06c555 100644 --- a/tests/ui/parser/assoc-const-underscore-semantic-fail.rs +++ b/tests/ui/parser/assoc/assoc-const-underscore-semantic-fail.rs diff --git a/tests/ui/parser/assoc-const-underscore-semantic-fail.stderr b/tests/ui/parser/assoc/assoc-const-underscore-semantic-fail.stderr index 538bf0ec100..538bf0ec100 100644 --- a/tests/ui/parser/assoc-const-underscore-semantic-fail.stderr +++ b/tests/ui/parser/assoc/assoc-const-underscore-semantic-fail.stderr diff --git a/tests/ui/parser/assoc-const-underscore-syntactic-pass.rs b/tests/ui/parser/assoc/assoc-const-underscore-syntactic-pass.rs index 60da408c811..60da408c811 100644 --- a/tests/ui/parser/assoc-const-underscore-syntactic-pass.rs +++ b/tests/ui/parser/assoc/assoc-const-underscore-syntactic-pass.rs diff --git a/tests/ui/parser/assoc-oddities-1.rs b/tests/ui/parser/assoc/assoc-oddities-1.rs index 5914805e5c1..5914805e5c1 100644 --- a/tests/ui/parser/assoc-oddities-1.rs +++ b/tests/ui/parser/assoc/assoc-oddities-1.rs diff --git a/tests/ui/parser/assoc-oddities-1.stderr b/tests/ui/parser/assoc/assoc-oddities-1.stderr index acf71b4893a..acf71b4893a 100644 --- a/tests/ui/parser/assoc-oddities-1.stderr +++ b/tests/ui/parser/assoc/assoc-oddities-1.stderr diff --git a/tests/ui/parser/assoc-oddities-2.rs b/tests/ui/parser/assoc/assoc-oddities-2.rs index 3d35aad7455..3d35aad7455 100644 --- a/tests/ui/parser/assoc-oddities-2.rs +++ b/tests/ui/parser/assoc/assoc-oddities-2.rs diff --git a/tests/ui/parser/assoc-oddities-2.stderr b/tests/ui/parser/assoc/assoc-oddities-2.stderr index d3b90c34c29..d3b90c34c29 100644 --- a/tests/ui/parser/assoc-oddities-2.stderr +++ b/tests/ui/parser/assoc/assoc-oddities-2.stderr diff --git a/tests/ui/parser/assoc-static-semantic-fail.rs b/tests/ui/parser/assoc/assoc-static-semantic-fail.rs index 403160f1253..403160f1253 100644 --- a/tests/ui/parser/assoc-static-semantic-fail.rs +++ b/tests/ui/parser/assoc/assoc-static-semantic-fail.rs diff --git a/tests/ui/parser/assoc-static-semantic-fail.stderr b/tests/ui/parser/assoc/assoc-static-semantic-fail.stderr index 8178bd22373..8178bd22373 100644 --- a/tests/ui/parser/assoc-static-semantic-fail.stderr +++ b/tests/ui/parser/assoc/assoc-static-semantic-fail.stderr diff --git a/tests/ui/parser/assoc-static-syntactic-fail.rs b/tests/ui/parser/assoc/assoc-static-syntactic-fail.rs index 492f2ea16ef..492f2ea16ef 100644 --- a/tests/ui/parser/assoc-static-syntactic-fail.rs +++ b/tests/ui/parser/assoc/assoc-static-syntactic-fail.rs diff --git a/tests/ui/parser/assoc-static-syntactic-fail.stderr b/tests/ui/parser/assoc/assoc-static-syntactic-fail.stderr index e9723614512..e9723614512 100644 --- a/tests/ui/parser/assoc-static-syntactic-fail.stderr +++ b/tests/ui/parser/assoc/assoc-static-syntactic-fail.stderr diff --git a/tests/ui/parser/assoc-type-in-type-arg.rs b/tests/ui/parser/assoc/assoc-type-in-type-arg.rs index 000956ea24f..000956ea24f 100644 --- a/tests/ui/parser/assoc-type-in-type-arg.rs +++ b/tests/ui/parser/assoc/assoc-type-in-type-arg.rs diff --git a/tests/ui/parser/assoc-type-in-type-arg.stderr b/tests/ui/parser/assoc/assoc-type-in-type-arg.stderr index b637702f21e..b637702f21e 100644 --- a/tests/ui/parser/assoc-type-in-type-arg.stderr +++ b/tests/ui/parser/assoc/assoc-type-in-type-arg.stderr diff --git a/tests/ui/parser/associated-types-project-from-hrtb-explicit.rs b/tests/ui/parser/assoc/associated-types-project-from-hrtb-explicit.rs index b238a9ca226..b238a9ca226 100644 --- a/tests/ui/parser/associated-types-project-from-hrtb-explicit.rs +++ b/tests/ui/parser/assoc/associated-types-project-from-hrtb-explicit.rs diff --git a/tests/ui/parser/associated-types-project-from-hrtb-explicit.stderr b/tests/ui/parser/assoc/associated-types-project-from-hrtb-explicit.stderr index aa0fa0e3c0a..aa0fa0e3c0a 100644 --- a/tests/ui/parser/associated-types-project-from-hrtb-explicit.stderr +++ b/tests/ui/parser/assoc/associated-types-project-from-hrtb-explicit.stderr diff --git a/tests/ui/parser/issues/issue-15980.rs b/tests/ui/parser/issues/issue-15980.rs index 87faa7d5ff1..eb7b6ca8296 100644 --- a/tests/ui/parser/issues/issue-15980.rs +++ b/tests/ui/parser/issues/issue-15980.rs @@ -9,9 +9,6 @@ fn main(){ //~^ ERROR expected identifier, found keyword `return` //~| NOTE expected identifier, found keyword } - //~^ NOTE expected one of `.`, `=>`, `?`, or an operator _ => {} - //~^ ERROR expected one of `.`, `=>`, `?`, or an operator, found reserved identifier `_` - //~| NOTE unexpected token } } diff --git a/tests/ui/parser/issues/issue-15980.stderr b/tests/ui/parser/issues/issue-15980.stderr index c59c811199e..cf8d0114787 100644 --- a/tests/ui/parser/issues/issue-15980.stderr +++ b/tests/ui/parser/issues/issue-15980.stderr @@ -11,15 +11,10 @@ help: escape `return` to use it as an identifier | LL | r#return | ++ - -error: expected one of `.`, `=>`, `?`, or an operator, found reserved identifier `_` - --> $DIR/issue-15980.rs:13:9 +help: you might have meant to start a match arm after the match guard | -LL | } - | - expected one of `.`, `=>`, `?`, or an operator -LL | -LL | _ => {} - | ^ unexpected token +LL | Err(ref e) if e.kind == io::EndOfFile => { + | ++ -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-52496.stderr b/tests/ui/parser/issues/issue-52496.stderr index 77335c64c21..78c81bf5b0d 100644 --- a/tests/ui/parser/issues/issue-52496.stderr +++ b/tests/ui/parser/issues/issue-52496.stderr @@ -8,10 +8,15 @@ error: expected one of `,`, `:`, or `}`, found `.` --> $DIR/issue-52496.rs:8:22 | LL | let _ = Foo { bar.into(), bat: -1, . }; - | --- - ^ expected one of `,`, `:`, or `}` + | --- ---^ expected one of `,`, `:`, or `}` | | | - | | help: try naming a field: `bar:` + | | while parsing this struct field | while parsing this struct + | +help: try naming a field + | +LL | let _ = Foo { bar: bar.into(), bat: -1, . }; + | ++++ error: expected identifier, found `.` --> $DIR/issue-52496.rs:8:40 diff --git a/tests/ui/parser/issues/issue-89396.fixed b/tests/ui/parser/issues/issue-89396.fixed index 823ad8cd1f8..0c040ddea44 100644 --- a/tests/ui/parser/issues/issue-89396.fixed +++ b/tests/ui/parser/issues/issue-89396.fixed @@ -8,9 +8,9 @@ fn main() { let _ = match opt { Some(_) => true, //~^ ERROR: expected one of - //~| HELP: try using a fat arrow here + //~| HELP: use a fat arrow to start a match arm None => false, //~^ ERROR: expected one of - //~| HELP: try using a fat arrow here + //~| HELP: use a fat arrow to start a match arm }; } diff --git a/tests/ui/parser/issues/issue-89396.rs b/tests/ui/parser/issues/issue-89396.rs index f1d9efa524f..d95f666d797 100644 --- a/tests/ui/parser/issues/issue-89396.rs +++ b/tests/ui/parser/issues/issue-89396.rs @@ -8,9 +8,9 @@ fn main() { let _ = match opt { Some(_) = true, //~^ ERROR: expected one of - //~| HELP: try using a fat arrow here + //~| HELP: use a fat arrow to start a match arm None -> false, //~^ ERROR: expected one of - //~| HELP: try using a fat arrow here + //~| HELP: use a fat arrow to start a match arm }; } diff --git a/tests/ui/parser/issues/issue-89396.stderr b/tests/ui/parser/issues/issue-89396.stderr index 504420574e2..41ce0705074 100644 --- a/tests/ui/parser/issues/issue-89396.stderr +++ b/tests/ui/parser/issues/issue-89396.stderr @@ -5,7 +5,7 @@ LL | Some(_) = true, | ^ | | | expected one of `=>`, `if`, or `|` - | help: try using a fat arrow here: `=>` + | help: use a fat arrow to start a match arm: `=>` error: expected one of `=>`, `@`, `if`, or `|`, found `->` --> $DIR/issue-89396.rs:12:14 @@ -14,7 +14,7 @@ LL | None -> false, | ^^ | | | expected one of `=>`, `@`, `if`, or `|` - | help: try using a fat arrow here: `=>` + | help: use a fat arrow to start a match arm: `=>` error: aborting due to 2 previous errors diff --git a/tests/ui/parser/missing-fat-arrow.rs b/tests/ui/parser/missing-fat-arrow.rs new file mode 100644 index 00000000000..325f1ccf2fd --- /dev/null +++ b/tests/ui/parser/missing-fat-arrow.rs @@ -0,0 +1,38 @@ +fn main() { + let x = 1; + let y = 2; + let value = 3; + + match value { + Some(x) if x == y { + self.next_token()?; //~ ERROR expected identifier, found keyword `self` + }, + _ => {} + } + let _: i32 = (); //~ ERROR mismatched types +} + +struct Foo { + value: usize +} + +fn foo(a: Option<&mut Foo>, b: usize) { + match a { + Some(a) if a.value == b { + a.value = 1; //~ ERROR expected one of `,`, `:`, or `}`, found `.` + }, + _ => {} + } + let _: i32 = (); //~ ERROR mismatched types +} + +fn bar(a: Option<&mut Foo>, b: usize) { + match a { + Some(a) if a.value == b { + a.value, //~ ERROR expected one of `,`, `:`, or `}`, found `.` + } => { + } + _ => {} + } + let _: i32 = (); //~ ERROR mismatched types +} diff --git a/tests/ui/parser/missing-fat-arrow.stderr b/tests/ui/parser/missing-fat-arrow.stderr new file mode 100644 index 00000000000..a6c786905e9 --- /dev/null +++ b/tests/ui/parser/missing-fat-arrow.stderr @@ -0,0 +1,78 @@ +error: expected identifier, found keyword `self` + --> $DIR/missing-fat-arrow.rs:8:13 + | +LL | Some(x) if x == y { + | - while parsing this struct +LL | self.next_token()?; + | ^^^^ expected identifier, found keyword + | +help: you might have meant to start a match arm after the match guard + | +LL | Some(x) if x == y => { + | ++ + +error: expected one of `,`, `:`, or `}`, found `.` + --> $DIR/missing-fat-arrow.rs:22:14 + | +LL | Some(a) if a.value == b { + | - while parsing this struct +LL | a.value = 1; + | -^ expected one of `,`, `:`, or `}` + | | + | while parsing this struct field + | +help: try naming a field + | +LL | a: a.value = 1; + | ++ +help: you might have meant to start a match arm after the match guard + | +LL | Some(a) if a.value == b => { + | ++ + +error: expected one of `,`, `:`, or `}`, found `.` + --> $DIR/missing-fat-arrow.rs:32:14 + | +LL | Some(a) if a.value == b { + | - while parsing this struct +LL | a.value, + | -^ expected one of `,`, `:`, or `}` + | | + | while parsing this struct field + | +help: try naming a field + | +LL | a: a.value, + | ++ +help: you might have meant to start a match arm after the match guard + | +LL | Some(a) if a.value == b => { + | ++ + +error[E0308]: mismatched types + --> $DIR/missing-fat-arrow.rs:12:18 + | +LL | let _: i32 = (); + | --- ^^ expected `i32`, found `()` + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/missing-fat-arrow.rs:26:18 + | +LL | let _: i32 = (); + | --- ^^ expected `i32`, found `()` + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/missing-fat-arrow.rs:37:18 + | +LL | let _: i32 = (); + | --- ^^ expected `i32`, found `()` + | | + | expected due to this + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/removed-syntax-with-2.stderr b/tests/ui/parser/removed-syntax-with-2.stderr index c6ae1ce674f..e75c5bcd643 100644 --- a/tests/ui/parser/removed-syntax-with-2.stderr +++ b/tests/ui/parser/removed-syntax-with-2.stderr @@ -2,8 +2,9 @@ error: expected one of `,`, `:`, or `}`, found `a` --> $DIR/removed-syntax-with-2.rs:8:31 | LL | let b = S { foo: (), with a }; - | - ^ expected one of `,`, `:`, or `}` - | | + | - ---- ^ expected one of `,`, `:`, or `}` + | | | + | | while parsing this struct field | while parsing this struct error[E0063]: missing field `bar` in initializer of `S` diff --git a/tests/ui/pattern/slice-patterns-irrefutable.stderr b/tests/ui/pattern/slice-patterns-irrefutable.stderr index fac99534f3e..e98ee28d686 100644 --- a/tests/ui/pattern/slice-patterns-irrefutable.stderr +++ b/tests/ui/pattern/slice-patterns-irrefutable.stderr @@ -3,6 +3,9 @@ error[E0282]: type annotations needed for `[_; 3]` | LL | let b; | ^ +LL | +LL | [a, b] = Default::default(); + | - type must be known at this point | help: consider giving `b` an explicit type, where the placeholders `_` are specified | diff --git a/tests/ui/suggestions/slice-issue-87994.stderr b/tests/ui/suggestions/slice-issue-87994.stderr index 84ecd749b0d..656f71eb877 100644 --- a/tests/ui/suggestions/slice-issue-87994.stderr +++ b/tests/ui/suggestions/slice-issue-87994.stderr @@ -1,4 +1,4 @@ -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..] { @@ -13,7 +13,7 @@ LL | for _ in &v[1..] { LL | for _ in &mut v[1..] { | ++++ -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..] { @@ -28,7 +28,7 @@ LL | for _ in &v[1..] { LL | for _ in &mut v[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..] { @@ -43,7 +43,7 @@ LL | for i2 in &v2[1..] { LL | for i2 in &mut v2[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..] { diff --git a/tests/ui/traits/copy-guessing.stderr b/tests/ui/traits/copy-guessing.stderr index 568b7e5a64a..7e676c9da01 100644 --- a/tests/ui/traits/copy-guessing.stderr +++ b/tests/ui/traits/copy-guessing.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed for `Option<T>` --> $DIR/copy-guessing.rs:20:9 | LL | let n = None; - | ^ + | ^ ---- type must be known at this point | help: consider giving `n` an explicit type, where the type for type parameter `T` is specified | diff --git a/tests/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs b/tests/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs index 9a444be500c..205b1173e07 100644 --- a/tests/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs +++ b/tests/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs @@ -14,7 +14,5 @@ impl<X> Method<u32> for Thing<X> { fn main() { let thing = Thing(true); - thing.method(42); - //~^ ERROR type annotations needed - //~| ERROR type annotations needed + thing.method(42); //~ ERROR type annotations needed } diff --git a/tests/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr b/tests/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr index 57b2587ae5c..2185c51e59d 100644 --- a/tests/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr +++ b/tests/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr @@ -1,14 +1,3 @@ -error[E0282]: type annotations needed - --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:17:11 - | -LL | thing.method(42); - | ^^^^^^ - | -help: try using a fully qualified path to specify the expected types - | -LL | <Thing<bool> as Method<T>>::method(thing, 42); - | +++++++++++++++++++++++++++++++++++ ~ - error[E0283]: type annotations needed --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:17:11 | @@ -28,7 +17,6 @@ help: try using a fully qualified path to specify the expected types LL | <Thing<bool> as Method<T>>::method(thing, 42); | +++++++++++++++++++++++++++++++++++ ~ -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0282, E0283. -For more information about an error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/issue-77982.rs b/tests/ui/traits/issue-77982.rs index f5be6cf21c1..2331dda9168 100644 --- a/tests/ui/traits/issue-77982.rs +++ b/tests/ui/traits/issue-77982.rs @@ -1,3 +1,4 @@ +// ignore-windows different list of satisfying impls use std::collections::HashMap; fn what() { diff --git a/tests/ui/traits/issue-77982.stderr b/tests/ui/traits/issue-77982.stderr index 33cc186ac0b..592cfd970c6 100644 --- a/tests/ui/traits/issue-77982.stderr +++ b/tests/ui/traits/issue-77982.stderr @@ -1,5 +1,5 @@ error[E0283]: type annotations needed - --> $DIR/issue-77982.rs:8:10 + --> $DIR/issue-77982.rs:9:10 | LL | opts.get(opt.as_ref()); | ^^^ ------------ type must be known at this point @@ -18,7 +18,7 @@ LL | opts.get::<Q>(opt.as_ref()); | +++++ error[E0283]: type annotations needed - --> $DIR/issue-77982.rs:8:10 + --> $DIR/issue-77982.rs:9:10 | LL | opts.get(opt.as_ref()); | ^^^ ------ type must be known at this point @@ -35,25 +35,34 @@ help: consider specifying the generic argument LL | opts.get::<Q>(opt.as_ref()); | +++++ -error[E0282]: type annotations needed - --> $DIR/issue-77982.rs:13:59 +error[E0283]: type annotations needed + --> $DIR/issue-77982.rs:14:59 | LL | let ips: Vec<_> = (0..100_000).map(|_| u32::from(0u32.into())).collect(); - | ^^^^ - | + | --- ^^^^ + | | + | type must be known at this point + | + = note: multiple `impl`s satisfying `u32: From<_>` found in the `core` crate: + - impl From<Ipv4Addr> for u32; + - impl From<NonZeroU32> for u32; + - impl From<bool> for u32; + - impl From<char> for u32; + - impl From<u16> for u32; + - impl From<u8> for u32; help: try using a fully qualified path to specify the expected types | LL | let ips: Vec<_> = (0..100_000).map(|_| u32::from(<u32 as Into<T>>::into(0u32))).collect(); | +++++++++++++++++++++++ ~ error[E0283]: type annotations needed for `Box<T>` - --> $DIR/issue-77982.rs:36:9 + --> $DIR/issue-77982.rs:37:9 | LL | let _ = ().foo(); | ^ --- type must be known at this point | note: multiple `impl`s satisfying `(): Foo<'_, _>` found - --> $DIR/issue-77982.rs:29:1 + --> $DIR/issue-77982.rs:30:1 | LL | impl Foo<'static, u32> for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,13 +74,13 @@ LL | let _: Box<T> = ().foo(); | ++++++++ error[E0283]: type annotations needed for `Box<T>` - --> $DIR/issue-77982.rs:40:9 + --> $DIR/issue-77982.rs:41:9 | LL | let _ = (&()).bar(); | ^ --- type must be known at this point | note: multiple `impl`s satisfying `&(): Bar<'_, _>` found - --> $DIR/issue-77982.rs:32:1 + --> $DIR/issue-77982.rs:33:1 | LL | impl<'a> Bar<'static, u32> for &'a () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,5 +93,4 @@ LL | let _: Box<T> = (&()).bar(); error: aborting due to 5 previous errors -Some errors have detailed explanations: E0282, E0283. -For more information about an error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/multidispatch-convert-ambig-dest.rs b/tests/ui/traits/multidispatch-convert-ambig-dest.rs index aa74e11c362..36e1e868fca 100644 --- a/tests/ui/traits/multidispatch-convert-ambig-dest.rs +++ b/tests/ui/traits/multidispatch-convert-ambig-dest.rs @@ -25,7 +25,6 @@ where T : Convert<U> fn a() { test(22, std::default::Default::default()); //~^ ERROR type annotations needed - //~| ERROR type annotations needed } fn main() {} diff --git a/tests/ui/traits/multidispatch-convert-ambig-dest.stderr b/tests/ui/traits/multidispatch-convert-ambig-dest.stderr index e927f26e96d..e3bfc78bb08 100644 --- a/tests/ui/traits/multidispatch-convert-ambig-dest.stderr +++ b/tests/ui/traits/multidispatch-convert-ambig-dest.stderr @@ -1,14 +1,3 @@ -error[E0282]: type annotations needed - --> $DIR/multidispatch-convert-ambig-dest.rs:26:5 - | -LL | test(22, std::default::Default::default()); - | ^^^^ cannot infer type of the type parameter `U` declared on the function `test` - | -help: consider specifying the generic arguments - | -LL | test::<i32, U>(22, std::default::Default::default()); - | ++++++++++ - error[E0283]: type annotations needed --> $DIR/multidispatch-convert-ambig-dest.rs:26:5 | @@ -37,7 +26,6 @@ help: consider specifying the generic arguments LL | test::<i32, U>(22, std::default::Default::default()); | ++++++++++ -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0282, E0283. -For more information about an error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/new-solver/alias-bound-unsound.rs b/tests/ui/traits/new-solver/alias-bound-unsound.rs index 38d83d289f1..825e874d71b 100644 --- a/tests/ui/traits/new-solver/alias-bound-unsound.rs +++ b/tests/ui/traits/new-solver/alias-bound-unsound.rs @@ -27,6 +27,5 @@ fn main() { //~| ERROR overflow evaluating the requirement `<() as Foo>::Item well-formed` //~| ERROR overflow evaluating the requirement `String <: <() as Foo>::Item` //~| ERROR overflow evaluating the requirement `&<() as Foo>::Item well-formed` - //~| ERROR type annotations needed println!("{x}"); } diff --git a/tests/ui/traits/new-solver/alias-bound-unsound.stderr b/tests/ui/traits/new-solver/alias-bound-unsound.stderr index abc6677c132..ca4b5c90ff2 100644 --- a/tests/ui/traits/new-solver/alias-bound-unsound.stderr +++ b/tests/ui/traits/new-solver/alias-bound-unsound.stderr @@ -11,18 +11,7 @@ note: required by a bound in `Foo::Item` LL | type Item: Copy | ^^^^ required by this bound in `Foo::Item` -error[E0282]: type annotations needed - --> $DIR/alias-bound-unsound.rs:24:5 - | -LL | drop(<() as Foo>::copy_me(&x)); - | ^^^^ cannot infer type of the type parameter `T` declared on the function `drop` - | -help: consider specifying the generic argument - | -LL | drop::<T>(<() as Foo>::copy_me(&x)); - | +++++ - -error[E0275]: overflow evaluating the requirement `&<() as Foo>::Item well-formed` +error[E0275]: overflow evaluating the requirement `String <: <() as Foo>::Item` --> $DIR/alias-bound-unsound.rs:24:31 | LL | drop(<() as Foo>::copy_me(&x)); @@ -30,40 +19,39 @@ LL | drop(<() as Foo>::copy_me(&x)); | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) -error[E0275]: overflow evaluating the requirement `String <: <() as Foo>::Item` - --> $DIR/alias-bound-unsound.rs:24:31 +error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` + --> $DIR/alias-bound-unsound.rs:24:10 | LL | drop(<() as Foo>::copy_me(&x)); - | ^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) -error[E0275]: overflow evaluating the requirement `<() as Foo>::Item well-formed` +error[E0275]: overflow evaluating the requirement `<() as Foo>::Item: Sized` --> $DIR/alias-bound-unsound.rs:24:10 | LL | drop(<() as Foo>::copy_me(&x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) + = note: the return type of a function must have a statically known size -error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` - --> $DIR/alias-bound-unsound.rs:24:10 +error[E0275]: overflow evaluating the requirement `&<() as Foo>::Item well-formed` + --> $DIR/alias-bound-unsound.rs:24:31 | LL | drop(<() as Foo>::copy_me(&x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) -error[E0275]: overflow evaluating the requirement `<() as Foo>::Item: Sized` +error[E0275]: overflow evaluating the requirement `<() as Foo>::Item well-formed` --> $DIR/alias-bound-unsound.rs:24:10 | LL | drop(<() as Foo>::copy_me(&x)); - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) - = note: the return type of a function must have a statically known size -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0275, E0282. -For more information about an error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/new-solver/assembly/runaway-impl-candidate-selection.stderr b/tests/ui/traits/new-solver/assembly/runaway-impl-candidate-selection.stderr index 47004821ad7..414deb47727 100644 --- a/tests/ui/traits/new-solver/assembly/runaway-impl-candidate-selection.stderr +++ b/tests/ui/traits/new-solver/assembly/runaway-impl-candidate-selection.stderr @@ -1,9 +1,16 @@ -error[E0282]: type annotations needed +error[E0283]: type annotations needed --> $DIR/runaway-impl-candidate-selection.rs:13:22 | LL | println!("{:?}", iter::<_>()); | ^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `iter` + | + = note: cannot satisfy `_: Iterator` +note: required by a bound in `iter` + --> $DIR/runaway-impl-candidate-selection.rs:8:12 + | +LL | fn iter<T: Iterator>() -> <T as Iterator>::Item { + | ^^^^^^^^ required by this bound in `iter` error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/new-solver/cycles/coinduction/fixpoint-exponential-growth.rs b/tests/ui/traits/new-solver/cycles/coinduction/fixpoint-exponential-growth.rs index fcafdcf637a..44e763ef990 100644 --- a/tests/ui/traits/new-solver/cycles/coinduction/fixpoint-exponential-growth.rs +++ b/tests/ui/traits/new-solver/cycles/coinduction/fixpoint-exponential-growth.rs @@ -27,6 +27,5 @@ fn impls<T: Trait>() {} fn main() { impls::<W<_>>(); - //~^ ERROR type annotations needed - //~| ERROR overflow evaluating the requirement + //~^ ERROR overflow evaluating the requirement } diff --git a/tests/ui/traits/new-solver/cycles/coinduction/fixpoint-exponential-growth.stderr b/tests/ui/traits/new-solver/cycles/coinduction/fixpoint-exponential-growth.stderr index a861156711d..1ac0e297729 100644 --- a/tests/ui/traits/new-solver/cycles/coinduction/fixpoint-exponential-growth.stderr +++ b/tests/ui/traits/new-solver/cycles/coinduction/fixpoint-exponential-growth.stderr @@ -1,9 +1,3 @@ -error[E0282]: type annotations needed - --> $DIR/fixpoint-exponential-growth.rs:29:5 - | -LL | impls::<W<_>>(); - | ^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls` - error[E0275]: overflow evaluating the requirement `W<_>: Trait` --> $DIR/fixpoint-exponential-growth.rs:29:13 | @@ -17,7 +11,6 @@ note: required by a bound in `impls` LL | fn impls<T: Trait>() {} | ^^^^^ required by this bound in `impls` -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0275, E0282. -For more information about an error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr index 9a8060133b8..9aa4f4531f9 100644 --- a/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr +++ b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr @@ -1,9 +1,18 @@ -error[E0282]: type annotations needed +error[E0284]: type annotations needed: cannot satisfy `<<Leaf as WithAssoc<_>>::Assoc as Id>::Assoc == <<Leaf as WithAssoc<_>>::Assoc as Id>::Assoc` --> $DIR/generalize-proj-new-universe-index-2.rs:74:5 | LL | bound::<<Rigid as IdHigherRankedBound>::Assoc, <Wrapper<Leaf> as Id>::Assoc, _>() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `V` declared on the function `bound` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<<Leaf as WithAssoc<_>>::Assoc as Id>::Assoc == <<Leaf as WithAssoc<_>>::Assoc as Id>::Assoc` + | +note: required by a bound in `bound` + --> $DIR/generalize-proj-new-universe-index-2.rs:69:21 + | +LL | fn bound<T: ?Sized, U: ?Sized, V: ?Sized>() + | ----- required by a bound in this function +LL | where +LL | T: WithAssoc<U, Assoc = V>, + | ^^^^^^^^^ required by this bound in `bound` error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/traits/new-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr b/tests/ui/traits/new-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr index 06283201261..f482e8cfaf9 100644 --- a/tests/ui/traits/new-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr +++ b/tests/ui/traits/new-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr @@ -1,9 +1,17 @@ -error[E0282]: type annotations needed +error[E0283]: type annotations needed --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:36:5 | LL | foo(unconstrained()) - | ^^^ cannot infer type of the type parameter `T` declared on the function `foo` + | ^^^ --------------- type must be known at this point + | | + | cannot infer type of the type parameter `T` declared on the function `foo` | + = note: cannot satisfy `_: Trait` +note: required by a bound in `foo` + --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:19:11 + | +LL | fn foo<T: Trait<Assoc = u8>>(x: T) {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `foo` help: consider specifying the generic argument | LL | foo::<T>(unconstrained()) @@ -11,4 +19,4 @@ LL | foo::<T>(unconstrained()) error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/new-solver/overflow/exponential-trait-goals.rs b/tests/ui/traits/new-solver/overflow/exponential-trait-goals.rs index b37f09ee185..3d2e70a639f 100644 --- a/tests/ui/traits/new-solver/overflow/exponential-trait-goals.rs +++ b/tests/ui/traits/new-solver/overflow/exponential-trait-goals.rs @@ -15,6 +15,5 @@ fn impls<T: Trait>() {} fn main() { impls::<W<_>>(); - //~^ ERROR type annotations needed - //~| ERROR overflow evaluating the requirement `W<_>: Trait` + //~^ ERROR overflow evaluating the requirement `W<_>: Trait` } diff --git a/tests/ui/traits/new-solver/overflow/exponential-trait-goals.stderr b/tests/ui/traits/new-solver/overflow/exponential-trait-goals.stderr index beed40f3649..023efc41aeb 100644 --- a/tests/ui/traits/new-solver/overflow/exponential-trait-goals.stderr +++ b/tests/ui/traits/new-solver/overflow/exponential-trait-goals.stderr @@ -1,9 +1,3 @@ -error[E0282]: type annotations needed - --> $DIR/exponential-trait-goals.rs:17:5 - | -LL | impls::<W<_>>(); - | ^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls` - error[E0275]: overflow evaluating the requirement `W<_>: Trait` --> $DIR/exponential-trait-goals.rs:17:13 | @@ -17,7 +11,6 @@ note: required by a bound in `impls` LL | fn impls<T: Trait>() {} | ^^^^^ required by this bound in `impls` -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0275, E0282. -For more information about an error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr index ed9b57cb1bd..d8db07277c5 100644 --- a/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr +++ b/tests/ui/traits/non_lifetime_binders/bad-sized-cond.stderr @@ -23,13 +23,13 @@ LL | where LL | for<V> V: Sized, | ^^^^^ required by this bound in `foo` -error[E0277]: the size for values of type `V` cannot be known at compilation time +error[E0277]: `V` is not an iterator --> $DIR/bad-sized-cond.rs:20:5 | LL | bar(); - | ^^^ doesn't have a size known at compile-time + | ^^^ `V` is not an iterator | - = help: the trait `Sized` is not implemented for `V` + = help: the trait `Iterator` is not implemented for `V` = note: required for `V` to implement `IntoIterator` note: required by a bound in `bar` --> $DIR/bad-sized-cond.rs:12:15 @@ -40,13 +40,13 @@ LL | where LL | for<V> V: IntoIterator, | ^^^^^^^^^^^^ required by this bound in `bar` -error[E0277]: `V` is not an iterator +error[E0277]: the size for values of type `V` cannot be known at compilation time --> $DIR/bad-sized-cond.rs:20:5 | LL | bar(); - | ^^^ `V` is not an iterator + | ^^^ doesn't have a size known at compile-time | - = help: the trait `Iterator` is not implemented for `V` + = help: the trait `Sized` is not implemented for `V` = note: required for `V` to implement `IntoIterator` note: required by a bound in `bar` --> $DIR/bad-sized-cond.rs:12:15 diff --git a/tests/ui/traits/not-suggest-non-existing-fully-qualified-path.rs b/tests/ui/traits/not-suggest-non-existing-fully-qualified-path.rs index 538e74ee1b0..e9e2f6b1290 100644 --- a/tests/ui/traits/not-suggest-non-existing-fully-qualified-path.rs +++ b/tests/ui/traits/not-suggest-non-existing-fully-qualified-path.rs @@ -18,7 +18,5 @@ where fn main() { let a = A(B); - a.method(); - //~^ ERROR type annotations needed - //~| ERROR type annotations needed + a.method(); //~ ERROR type annotations needed } diff --git a/tests/ui/traits/not-suggest-non-existing-fully-qualified-path.stderr b/tests/ui/traits/not-suggest-non-existing-fully-qualified-path.stderr index 92d9d32cf9c..86ae49b32fc 100644 --- a/tests/ui/traits/not-suggest-non-existing-fully-qualified-path.stderr +++ b/tests/ui/traits/not-suggest-non-existing-fully-qualified-path.stderr @@ -1,14 +1,3 @@ -error[E0282]: type annotations needed - --> $DIR/not-suggest-non-existing-fully-qualified-path.rs:21:7 - | -LL | a.method(); - | ^^^^^^ - | -help: try using a fully qualified path to specify the expected types - | -LL | <A<B> as V<U>>::method(a); - | +++++++++++++++++++++++ ~ - error[E0283]: type annotations needed --> $DIR/not-suggest-non-existing-fully-qualified-path.rs:21:7 | @@ -35,7 +24,6 @@ help: try using a fully qualified path to specify the expected types LL | <A<B> as V<U>>::method(a); | +++++++++++++++++++++++ ~ -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0282, E0283. -For more information about an error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/suggest-fully-qualified-closure.stderr b/tests/ui/traits/suggest-fully-qualified-closure.stderr index 2aea3783fea..43cef7027a2 100644 --- a/tests/ui/traits/suggest-fully-qualified-closure.stderr +++ b/tests/ui/traits/suggest-fully-qualified-closure.stderr @@ -1,14 +1,3 @@ -error[E0282]: type annotations needed - --> $DIR/suggest-fully-qualified-closure.rs:23:7 - | -LL | q.lol(||()); - | ^^^ - | -help: try using a fully qualified path to specify the expected types - | -LL | <Qqq as MyTrait<T>>::lol::<{closure@}>(&q, ||()); - | +++ ~ - error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-closure.rs:23:7 | @@ -28,7 +17,6 @@ help: try using a fully qualified path to specify the expected types LL | <Qqq as MyTrait<T>>::lol::<{closure@}>(&q, ||()); | +++ ~ -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0282, E0283. -For more information about an error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/suggest-fully-qualified-path-with-adjustment.rs b/tests/ui/traits/suggest-fully-qualified-path-with-adjustment.rs index 9a2cf469d08..f0861857bf7 100644 --- a/tests/ui/traits/suggest-fully-qualified-path-with-adjustment.rs +++ b/tests/ui/traits/suggest-fully-qualified-path-with-adjustment.rs @@ -42,9 +42,7 @@ impl<T> DerefMut for DerefsTo<T> { fn main() { let mut thing = Thing; - thing.method(); - //~^ ERROR type annotations needed - //~| ERROR type annotations needed + thing.method(); //~ ERROR type annotations needed thing.mut_method(); //~ ERROR type annotations needed thing.by_self(); //~ ERROR type annotations needed diff --git a/tests/ui/traits/suggest-fully-qualified-path-with-adjustment.stderr b/tests/ui/traits/suggest-fully-qualified-path-with-adjustment.stderr index 68b31a1ca34..841acb5ffd3 100644 --- a/tests/ui/traits/suggest-fully-qualified-path-with-adjustment.stderr +++ b/tests/ui/traits/suggest-fully-qualified-path-with-adjustment.stderr @@ -1,14 +1,3 @@ -error[E0282]: type annotations needed - --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:45:11 - | -LL | thing.method(); - | ^^^^^^ - | -help: try using a fully qualified path to specify the expected types - | -LL | <Thing as Method<T>>::method(&thing); - | ++++++++++++++++++++++++++++++ ~ - error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:45:11 | @@ -29,7 +18,7 @@ LL | <Thing as Method<T>>::method(&thing); | ++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:48:11 + --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:46:11 | LL | thing.mut_method(); | ^^^^^^^^^^ @@ -48,7 +37,7 @@ LL | <Thing as Method<T>>::mut_method(&mut thing); | +++++++++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:49:11 + --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:47:11 | LL | thing.by_self(); | ^^^^^^^ @@ -67,7 +56,7 @@ LL | <&Thing as MethodRef<T>>::by_self(&thing); | +++++++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:52:14 + --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:50:14 | LL | deref_to.method(); | ^^^^^^ @@ -86,7 +75,7 @@ LL | <Thing as Method<T>>::method(&deref_to); | ++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:53:14 + --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:51:14 | LL | deref_to.mut_method(); | ^^^^^^^^^^ @@ -105,7 +94,7 @@ LL | <Thing as Method<T>>::mut_method(&mut deref_to); | +++++++++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:54:14 + --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:52:14 | LL | deref_to.by_self(); | ^^^^^^^ @@ -124,7 +113,7 @@ LL | <&Thing as MethodRef<T>>::by_self(&deref_to); | +++++++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:57:20 + --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:55:20 | LL | deref_deref_to.method(); | ^^^^^^ @@ -143,7 +132,7 @@ LL | <Thing as Method<T>>::method(&deref_deref_to); | ++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:58:20 + --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:56:20 | LL | deref_deref_to.mut_method(); | ^^^^^^^^^^ @@ -162,7 +151,7 @@ LL | <Thing as Method<T>>::mut_method(&mut deref_deref_to); | +++++++++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:59:20 + --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:57:20 | LL | deref_deref_to.by_self(); | ^^^^^^^ @@ -180,7 +169,6 @@ help: try using a fully qualified path to specify the expected types LL | <&Thing as MethodRef<T>>::by_self(&deref_deref_to); | +++++++++++++++++++++++++++++++++++ ~ -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors -Some errors have detailed explanations: E0282, E0283. -For more information about an error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/suggest-fully-qualified-path-without-adjustment.rs b/tests/ui/traits/suggest-fully-qualified-path-without-adjustment.rs index da640c8c8c2..6a63e27f75f 100644 --- a/tests/ui/traits/suggest-fully-qualified-path-without-adjustment.rs +++ b/tests/ui/traits/suggest-fully-qualified-path-without-adjustment.rs @@ -42,9 +42,7 @@ impl<T> DerefMut for DerefsTo<T> { fn main() { let mut ref_thing = &Thing; - ref_thing.method(); - //~^ ERROR type annotations needed - //~| ERROR type annotations needed + ref_thing.method(); //~ ERROR type annotations needed ref_thing.by_self(); //~ ERROR type annotations needed let mut mut_thing = &mut Thing; diff --git a/tests/ui/traits/suggest-fully-qualified-path-without-adjustment.stderr b/tests/ui/traits/suggest-fully-qualified-path-without-adjustment.stderr index 27518a54e75..1865d81bad1 100644 --- a/tests/ui/traits/suggest-fully-qualified-path-without-adjustment.stderr +++ b/tests/ui/traits/suggest-fully-qualified-path-without-adjustment.stderr @@ -1,14 +1,3 @@ -error[E0282]: type annotations needed - --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:45:15 - | -LL | ref_thing.method(); - | ^^^^^^ - | -help: try using a fully qualified path to specify the expected types - | -LL | <Thing as Method<T>>::method(ref_thing); - | +++++++++++++++++++++++++++++ ~ - error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:45:15 | @@ -29,7 +18,7 @@ LL | <Thing as Method<T>>::method(ref_thing); | +++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:48:15 + --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:46:15 | LL | ref_thing.by_self(); | ^^^^^^^ @@ -48,7 +37,7 @@ LL | <&Thing as MethodRef<T>>::by_self(ref_thing); | ++++++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:51:15 + --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:49:15 | LL | mut_thing.method(); | ^^^^^^ @@ -67,7 +56,7 @@ LL | <Thing as Method<T>>::method(mut_thing); | +++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:52:15 + --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:50:15 | LL | mut_thing.mut_method(); | ^^^^^^^^^^ @@ -86,7 +75,7 @@ LL | <Thing as Method<T>>::mut_method(mut_thing); | +++++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:53:15 + --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:51:15 | LL | mut_thing.by_self(); | ^^^^^^^ @@ -105,7 +94,7 @@ LL | <&Thing as MethodRef<T>>::by_self(mut_thing); | ++++++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:56:14 + --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:54:14 | LL | deref_to.method(); | ^^^^^^ @@ -124,7 +113,7 @@ LL | <Thing as Method<T>>::method(deref_to); | +++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:57:14 + --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:55:14 | LL | deref_to.mut_method(); | ^^^^^^^^^^ @@ -143,7 +132,7 @@ LL | <Thing as Method<T>>::mut_method(deref_to); | +++++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:58:14 + --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:56:14 | LL | deref_to.by_self(); | ^^^^^^^ @@ -162,7 +151,7 @@ LL | <&Thing as MethodRef<T>>::by_self(deref_to); | ++++++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:61:20 + --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:59:20 | LL | deref_deref_to.method(); | ^^^^^^ @@ -181,7 +170,7 @@ LL | <Thing as Method<T>>::method(deref_deref_to); | +++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:62:20 + --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:60:20 | LL | deref_deref_to.mut_method(); | ^^^^^^^^^^ @@ -200,7 +189,7 @@ LL | <Thing as Method<T>>::mut_method(deref_deref_to); | +++++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:63:20 + --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:61:20 | LL | deref_deref_to.by_self(); | ^^^^^^^ @@ -218,7 +207,6 @@ help: try using a fully qualified path to specify the expected types LL | <&Thing as MethodRef<T>>::by_self(deref_deref_to); | ++++++++++++++++++++++++++++++++++ ~ -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors -Some errors have detailed explanations: E0282, E0283. -For more information about an error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/type-inference/sort_by_key.stderr b/tests/ui/type-inference/sort_by_key.stderr index de7b4b24899..81af024b3fb 100644 --- a/tests/ui/type-inference/sort_by_key.stderr +++ b/tests/ui/type-inference/sort_by_key.stderr @@ -1,9 +1,14 @@ -error[E0282]: type annotations needed +error[E0283]: type annotations needed --> $DIR/sort_by_key.rs:3:40 | LL | lst.sort_by_key(|&(v, _)| v.iter().sum()); - | ^^^ cannot infer type of the type parameter `S` declared on the method `sum` + | ----------- ^^^ cannot infer type of the type parameter `S` declared on the method `sum` + | | + | type must be known at this point | + = note: cannot satisfy `_: Ord` +note: required by a bound in `slice::<impl [T]>::sort_by_key` + --> $SRC_DIR/alloc/src/slice.rs:LL:COL help: consider specifying the generic argument | LL | lst.sort_by_key(|&(v, _)| v.iter().sum::<S>()); @@ -11,4 +16,4 @@ LL | lst.sort_by_key(|&(v, _)| v.iter().sum::<S>()); error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr b/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr index 09c4b2053b2..7be00341d10 100644 --- a/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr +++ b/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed for `Vec<_>` --> $DIR/cannot_infer_local_or_vec.rs:2:9 | LL | let x = vec![]; - | ^ + | ^ ------ type must be known at this point | help: consider giving `x` an explicit type, where the placeholders `_` are specified | diff --git a/tests/ui/wf/wf-fn-where-clause.stderr b/tests/ui/wf/wf-fn-where-clause.stderr index 2aec641e71e..cd6c051feed 100644 --- a/tests/ui/wf/wf-fn-where-clause.stderr +++ b/tests/ui/wf/wf-fn-where-clause.stderr @@ -14,6 +14,15 @@ help: consider further restricting type parameter `U` LL | fn foo<T,U>() where T: ExtraCopy<U>, U: std::marker::Copy | ++++++++++++++++++++++ +error[E0038]: the trait `Copy` cannot be made into an object + --> $DIR/wf-fn-where-clause.rs:12:16 + | +LL | fn bar() where Vec<dyn Copy>:, {} + | ^^^^^^^^^^^^^ `Copy` cannot be made into an object + | + = note: the trait cannot be made into an object because it requires `Self: Sized` + = note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + error[E0277]: the size for values of type `(dyn Copy + 'static)` cannot be known at compilation time --> $DIR/wf-fn-where-clause.rs:12:16 | @@ -34,15 +43,6 @@ LL | struct Vec<T> { LL | t: T, | - ...if indirection were used here: `Box<T>` -error[E0038]: the trait `Copy` cannot be made into an object - --> $DIR/wf-fn-where-clause.rs:12:16 - | -LL | fn bar() where Vec<dyn Copy>:, {} - | ^^^^^^^^^^^^^ `Copy` cannot be made into an object - | - = note: the trait cannot be made into an object because it requires `Self: Sized` - = note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - error: aborting due to 3 previous errors Some errors have detailed explanations: E0038, E0277. |
