diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 4abb1d90f44..b6b242bfc27 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1316,6 +1316,8 @@ fn lower_generics( param.id, ¶m.kind, ¶m.bounds, + param.colon_span, + generics.span, itctx, PredicateOrigin::GenericParam, ) @@ -1365,6 +1367,8 @@ pub(super) fn lower_generic_bound_predicate( id: NodeId, kind: &GenericParamKind, bounds: &[GenericBound], + colon_span: Option, + parent_span: Span, itctx: &ImplTraitContext, origin: PredicateOrigin, ) -> Option> { @@ -1377,21 +1381,17 @@ pub(super) fn lower_generic_bound_predicate( let ident = self.lower_ident(ident); let param_span = ident.span; - let span = bounds - .iter() - .fold(Some(param_span.shrink_to_hi()), |span: Option, bound| { - let bound_span = bound.span(); - // We include bounds that come from a `#[derive(_)]` but point at the user's code, - // as we use this method to get a span appropriate for suggestions. - if !bound_span.can_be_used_for_suggestions() { - None - } else if let Some(span) = span { - Some(span.to(bound_span)) - } else { - Some(bound_span) - } - }) - .unwrap_or(param_span.shrink_to_hi()); + + // Reconstruct the span of the entire predicate from the individual generic bounds. + let span_start = colon_span.unwrap_or_else(|| param_span.shrink_to_hi()); + let span = bounds.iter().fold(span_start, |span_accum, bound| { + match bound.span().find_ancestor_inside(parent_span) { + Some(bound_span) => span_accum.to(bound_span), + None => span_accum, + } + }); + let span = self.lower_span(span); + match kind { GenericParamKind::Const { .. } => None, GenericParamKind::Type { .. } => { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a089510493e..1fbc45a58e5 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2245,6 +2245,7 @@ fn lower_generic_and_bounds( ) -> (hir::GenericParam<'hir>, Option>, hir::TyKind<'hir>) { // Add a definition for the in-band `Param`. let def_id = self.local_def_id(node_id); + let span = self.lower_span(span); // Set the name to `impl Bound1 + Bound2`. let param = hir::GenericParam { @@ -2252,7 +2253,7 @@ fn lower_generic_and_bounds( def_id, name: ParamName::Plain(self.lower_ident(ident)), pure_wrt_drop: false, - span: self.lower_span(span), + span, kind: hir::GenericParamKind::Type { default: None, synthetic: true }, colon_span: None, }; @@ -2262,6 +2263,8 @@ fn lower_generic_and_bounds( node_id, &GenericParamKind::Type { default: None }, bounds, + /* colon_span */ None, + span, &ImplTraitContext::Universal, hir::PredicateOrigin::ImplTrait, ); @@ -2271,7 +2274,7 @@ fn lower_generic_and_bounds( let ty = hir::TyKind::Path(hir::QPath::Resolved( None, self.arena.alloc(hir::Path { - span: self.lower_span(span), + span, res, segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)], diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 0335c45a946..1360383a75a 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -74,6 +74,7 @@ pub fn emit_coerce_suggestions( self.note_type_is_not_clone(err, expected, expr_ty, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); self.note_internal_mutation_in_method(err, expr, expected, expr_ty); + self.check_for_range_as_method_call(err, expr, expr_ty, expected); } /// Requires that the two types unify, and prints an error message if @@ -1607,4 +1608,69 @@ pub fn check_for_cast( _ => false, } } + + /// Identify when the user has written `foo..bar()` instead of `foo.bar()`. + pub fn check_for_range_as_method_call( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + checked_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) { + if !hir::is_range_literal(expr) { + return; + } + let hir::ExprKind::Struct( + hir::QPath::LangItem(LangItem::Range, ..), + [start, end], + _, + ) = expr.kind else { return; }; + let parent = self.tcx.hir().get_parent_node(expr.hir_id); + if let Some(hir::Node::ExprField(_)) = self.tcx.hir().find(parent) { + // Ignore `Foo { field: a..Default::default() }` + return; + } + let mut expr = end.expr; + while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind { + // Getting to the root receiver and asserting it is a fn call let's us ignore cases in + // `src/test/ui/methods/issues/issue-90315.stderr`. + expr = rcvr; + } + let hir::ExprKind::Call(method_name, _) = expr.kind else { return; }; + let ty::Adt(adt, _) = checked_ty.kind() else { return; }; + if self.tcx.lang_items().range_struct() != Some(adt.did()) { + return; + } + if let ty::Adt(adt, _) = expected_ty.kind() + && self.tcx.lang_items().range_struct() == Some(adt.did()) + { + return; + } + // Check if start has method named end. + let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else { return; }; + let [hir::PathSegment { ident, .. }] = p.segments else { return; }; + let self_ty = self.typeck_results.borrow().expr_ty(start.expr); + let Ok(_pick) = self.probe_for_name( + probe::Mode::MethodCall, + *ident, + probe::IsSuggestion(true), + self_ty, + expr.hir_id, + probe::ProbeScope::AllTraits, + ) else { return; }; + let mut sugg = "."; + let mut span = start.expr.span.between(end.expr.span); + if span.lo() + BytePos(2) == span.hi() { + // There's no space between the start, the range op and the end, suggest removal which + // will be more noticeable than the replacement of `..` with `.`. + span = span.with_lo(span.lo() + BytePos(1)); + sugg = ""; + } + err.span_suggestion_verbose( + span, + "you likely meant to write a method call instead of a range", + sugg, + Applicability::MachineApplicable, + ); + } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 9a7d753e66b..e38d1bccc10 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -19,6 +19,7 @@ TypeVisitable, }; use rustc_session::errors::ExprParenthesesNeeded; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; @@ -1259,6 +1260,31 @@ pub(crate) fn suggest_floating_point_literal( ); true } + ExprKind::Lit(Spanned { + node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed), + span, + }) => { + let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) else { return false; }; + if !(snippet.starts_with("0x") || snippet.starts_with("0X")) { + return false; + } + if snippet.len() <= 5 || !snippet.is_char_boundary(snippet.len() - 3) { + return false; + } + let (_, suffix) = snippet.split_at(snippet.len() - 3); + let value = match suffix { + "f32" => (lit - 0xf32) / (16 * 16 * 16), + "f64" => (lit - 0xf64) / (16 * 16 * 16), + _ => return false, + }; + err.span_suggestions( + expr.span, + "rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float", + [format!("0x{value:X} as {suffix}"), format!("{value}_{suffix}")], + Applicability::MaybeIncorrect, + ); + true + } _ => false, } } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index d6de6e70ead..cdb901b7f86 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2184,6 +2184,7 @@ fn collect_outlives_bound_spans<'tcx>( tcx: TyCtxt<'tcx>, bounds: &hir::GenericBounds<'_>, inferred_outlives: &[ty::Region<'tcx>], + predicate_span: Span, ) -> Vec<(usize, Span)> { use rustc_middle::middle::resolve_lifetime::Region; @@ -2191,23 +2192,28 @@ fn collect_outlives_bound_spans<'tcx>( .iter() .enumerate() .filter_map(|(i, bound)| { - if let hir::GenericBound::Outlives(lifetime) = bound { - let is_inferred = match tcx.named_region(lifetime.hir_id) { - Some(Region::EarlyBound(def_id)) => inferred_outlives.iter().any(|r| { - if let ty::ReEarlyBound(ebr) = **r { - ebr.def_id == def_id - } else { - false - } - }), - _ => false, - }; - is_inferred.then_some((i, bound.span())) - } else { - None + let hir::GenericBound::Outlives(lifetime) = bound else { + return None; + }; + + let is_inferred = match tcx.named_region(lifetime.hir_id) { + Some(Region::EarlyBound(def_id)) => inferred_outlives + .iter() + .any(|r| matches!(**r, ty::ReEarlyBound(ebr) if { ebr.def_id == def_id })), + _ => false, + }; + + if !is_inferred { + return None; } + + let span = bound.span().find_ancestor_inside(predicate_span)?; + if in_external_macro(tcx.sess, span) { + return None; + } + + Some((i, span)) }) - .filter(|(_, span)| !in_external_macro(tcx.sess, *span)) .collect() } @@ -2273,9 +2279,9 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { use rustc_middle::middle::resolve_lifetime::Region; let def_id = item.owner_id.def_id; - if let hir::ItemKind::Struct(_, ref hir_generics) - | hir::ItemKind::Enum(_, ref hir_generics) - | hir::ItemKind::Union(_, ref hir_generics) = item.kind + if let hir::ItemKind::Struct(_, hir_generics) + | hir::ItemKind::Enum(_, hir_generics) + | hir::ItemKind::Union(_, hir_generics) = item.kind { let inferred_outlives = cx.tcx.inferred_outlives_of(def_id); if inferred_outlives.is_empty() { @@ -2290,53 +2296,58 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let mut dropped_predicate_count = 0; let num_predicates = hir_generics.predicates.len(); for (i, where_predicate) in hir_generics.predicates.iter().enumerate() { - let (relevant_lifetimes, bounds, span, in_where_clause) = match where_predicate { - hir::WherePredicate::RegionPredicate(predicate) => { - if let Some(Region::EarlyBound(region_def_id)) = - cx.tcx.named_region(predicate.lifetime.hir_id) - { - ( - Self::lifetimes_outliving_lifetime( - inferred_outlives, - region_def_id, - ), - &predicate.bounds, - predicate.span, - predicate.in_where_clause, - ) - } else { - continue; - } - } - hir::WherePredicate::BoundPredicate(predicate) => { - // FIXME we can also infer bounds on associated types, - // and should check for them here. - match predicate.bounded_ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => { - let Res::Def(DefKind::TyParam, def_id) = path.res else { - continue - }; - let index = ty_generics.param_def_id_to_index[&def_id]; + let (relevant_lifetimes, bounds, predicate_span, in_where_clause) = + match where_predicate { + hir::WherePredicate::RegionPredicate(predicate) => { + if let Some(Region::EarlyBound(region_def_id)) = + cx.tcx.named_region(predicate.lifetime.hir_id) + { ( - Self::lifetimes_outliving_type(inferred_outlives, index), + Self::lifetimes_outliving_lifetime( + inferred_outlives, + region_def_id, + ), &predicate.bounds, predicate.span, - predicate.origin == PredicateOrigin::WhereClause, + predicate.in_where_clause, ) - } - _ => { + } else { continue; } } - } - _ => continue, - }; + hir::WherePredicate::BoundPredicate(predicate) => { + // FIXME we can also infer bounds on associated types, + // and should check for them here. + match predicate.bounded_ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(None, path)) => { + let Res::Def(DefKind::TyParam, def_id) = path.res else { + continue; + }; + let index = ty_generics.param_def_id_to_index[&def_id]; + ( + Self::lifetimes_outliving_type(inferred_outlives, index), + &predicate.bounds, + predicate.span, + predicate.origin == PredicateOrigin::WhereClause, + ) + } + _ => { + continue; + } + } + } + _ => continue, + }; if relevant_lifetimes.is_empty() { continue; } - let bound_spans = - self.collect_outlives_bound_spans(cx.tcx, bounds, &relevant_lifetimes); + let bound_spans = self.collect_outlives_bound_spans( + cx.tcx, + bounds, + &relevant_lifetimes, + predicate_span, + ); bound_count += bound_spans.len(); let drop_predicate = bound_spans.len() == bounds.len(); @@ -2345,15 +2356,15 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { } if drop_predicate && !in_where_clause { - lint_spans.push(span); + lint_spans.push(predicate_span); } else if drop_predicate && i + 1 < num_predicates { // If all the bounds on a predicate were inferable and there are // further predicates, we want to eat the trailing comma. let next_predicate_span = hir_generics.predicates[i + 1].span(); - where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo())); + where_lint_spans.push(predicate_span.to(next_predicate_span.shrink_to_lo())); } else { where_lint_spans.extend(self.consolidate_outlives_bound_spans( - span.shrink_to_lo(), + predicate_span.shrink_to_lo(), bounds, bound_spans, )); @@ -2374,12 +2385,26 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { } else { hir_generics.span.shrink_to_hi().to(where_span) }; - lint_spans.push(full_where_span); + + // Due to macro expansions, the `full_where_span` might not actually contain all predicates. + if where_lint_spans.iter().all(|&sp| full_where_span.contains(sp)) { + lint_spans.push(full_where_span); + } else { + lint_spans.extend(where_lint_spans); + } } else { lint_spans.extend(where_lint_spans); } if !lint_spans.is_empty() { + // Do not automatically delete outlives requirements from macros. + let applicability = if lint_spans.iter().all(|sp| sp.can_be_used_for_suggestions()) + { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + cx.struct_span_lint( EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), @@ -2387,11 +2412,8 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { |lint| { lint.set_arg("count", bound_count).multipart_suggestion( fluent::suggestion, - lint_spans - .into_iter() - .map(|span| (span, String::new())) - .collect::>(), - Applicability::MachineApplicable, + lint_spans.into_iter().map(|span| (span, String::new())).collect(), + applicability, ) }, ); diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 3f6e29ad611..a633201e3d9 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -160,9 +160,13 @@ pub fn provide(providers: &mut Providers) { } else if let Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(_, TraitFn::Required(idents)), .. + }) + | Node::ForeignItem(&ForeignItem { + kind: ForeignItemKind::Fn(_, idents, _), + .. }) = hir.get(hir_id) { - tcx.arena.alloc_slice(idents) + idents } else { span_bug!(hir.span(hir_id), "fn_arg_names: unexpected item {:?}", id); } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 5b7a00101e9..49e069f58c9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -16,7 +16,7 @@ use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor}; use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_errors::{DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg}; +use rustc_errors::{Applicability, DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg}; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; @@ -536,6 +536,9 @@ struct DiagnosticMetadata<'ast> { in_assignment: Option<&'ast Expr>, is_assign_rhs: bool, + /// Used to detect possible `.` -> `..` typo when calling methods. + in_range: Option<(&'ast Expr, &'ast Expr)>, + /// If we are currently in a trait object definition. Used to point at the bounds when /// encountering a struct or enum. current_trait_object: Option<&'ast [ast::GenericBound]>, @@ -3320,6 +3323,7 @@ fn smart_resolve_path( ); } + #[instrument(level = "debug", skip(self))] fn smart_resolve_path_fragment( &mut self, qself: &Option>, @@ -3327,10 +3331,6 @@ fn smart_resolve_path_fragment( source: PathSource<'ast>, finalize: Finalize, ) -> PartialRes { - debug!( - "smart_resolve_path_fragment(qself={:?}, path={:?}, finalize={:?})", - qself, path, finalize, - ); let ns = source.namespace(); let Finalize { node_id, path_span, .. } = finalize; @@ -3341,8 +3341,28 @@ fn smart_resolve_path_fragment( let def_id = this.parent_scope.module.nearest_parent_mod(); let instead = res.is_some(); - let suggestion = - if res.is_none() { this.report_missing_type_error(path) } else { None }; + let suggestion = if let Some((start, end)) = this.diagnostic_metadata.in_range + && path[0].ident.span.lo() == end.span.lo() + { + let mut sugg = "."; + let mut span = start.span.between(end.span); + if span.lo() + BytePos(2) == span.hi() { + // There's no space between the start, the range op and the end, suggest + // removal which will look better. + span = span.with_lo(span.lo() + BytePos(1)); + sugg = ""; + } + Some(( + span, + "you might have meant to write `.` instead of `..`", + sugg.to_string(), + Applicability::MaybeIncorrect, + )) + } else if res.is_none() { + this.report_missing_type_error(path) + } else { + None + }; this.r.use_injections.push(UseError { err, @@ -4005,6 +4025,12 @@ fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) { self.visit_expr(rhs); self.diagnostic_metadata.is_assign_rhs = false; } + ExprKind::Range(Some(ref start), Some(ref end), RangeLimits::HalfOpen) => { + self.diagnostic_metadata.in_range = Some((start, end)); + self.resolve_expr(start, Some(expr)); + self.resolve_expr(end, Some(expr)); + self.diagnostic_metadata.in_range = None; + } _ => { visit::walk_expr(self, expr); } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 2181c090027..bee4b0a2332 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -796,6 +796,9 @@ pub fn macro_backtrace(mut self) -> impl Iterator { /// Returns a `Span` that would enclose both `self` and `end`. /// + /// Note that this can also be used to extend the span "backwards": + /// `start.to(end)` and `end.to(start)` return the same `Span`. + /// /// ```text /// ____ ___ /// self lorem ipsum end diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index ba34ab6800f..1028bb981b9 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -166,7 +166,7 @@ /// vec[0] = 7; /// assert_eq!(vec[0], 7); /// -/// vec.extend([1, 2, 3].iter().copied()); +/// vec.extend([1, 2, 3]); /// /// for x in &vec { /// println!("{x}"); diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index b4e173ce03d..ba93165cf53 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1783,7 +1783,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// until the reference expires. As a special exception, given an `&T`, any part of it that is /// inside an `UnsafeCell<_>` may be deallocated during the lifetime of the reference, after the /// last time the reference is used (dereferenced or reborrowed). Since you cannot deallocate a part -/// of what a reference points to, this means the memory an `&T` points to can be deallocted only if +/// of what a reference points to, this means the memory an `&T` points to can be deallocated only if /// *every part of it* (including padding) is inside an `UnsafeCell`. /// /// However, whenever a `&UnsafeCell` is constructed or dereferenced, it must still point to diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs index b355d94ce49..19f80daef1d 100644 --- a/library/core/src/cell/lazy.rs +++ b/library/core/src/cell/lazy.rs @@ -35,7 +35,7 @@ pub struct LazyCell T> { init: Cell>, } -impl LazyCell { +impl T> LazyCell { /// Creates a new lazy value with the given initializing function. /// /// # Examples @@ -55,9 +55,7 @@ impl LazyCell { pub const fn new(init: F) -> LazyCell { LazyCell { cell: OnceCell::new(), init: Cell::new(Some(init)) } } -} -impl T> LazyCell { /// Forces the evaluation of this lazy value and returns a reference to /// the result. /// diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index bac836292f8..08abb0b0d92 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -803,7 +803,7 @@ fn map(self, f: F) -> Map /// (0..5).map(|x| x * 2 + 1) /// .for_each(move |x| tx.send(x).unwrap()); /// - /// let v: Vec<_> = rx.iter().collect(); + /// let v: Vec<_> = rx.iter().collect(); /// assert_eq!(v, vec![1, 3, 5, 7, 9]); /// ``` /// diff --git a/library/core/tests/lazy.rs b/library/core/tests/lazy.rs index 70fcc6d2d4b..c7c3c479b71 100644 --- a/library/core/tests/lazy.rs +++ b/library/core/tests/lazy.rs @@ -106,6 +106,12 @@ fn lazy_new() { assert_eq!(called.get(), 1); } +// Check that we can infer `T` from closure's type. +#[test] +fn lazy_type_inference() { + let _ = LazyCell::new(|| ()); +} + #[test] fn aliasing_in_get() { let x = OnceCell::new(); diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index c8d3289ca4a..bf5a716fa03 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -46,17 +46,14 @@ pub struct LazyLock T> { cell: OnceLock, init: Cell>, } - -impl LazyLock { +impl T> LazyLock { /// Creates a new lazy value with the given initializing /// function. #[unstable(feature = "once_cell", issue = "74465")] pub const fn new(f: F) -> LazyLock { LazyLock { cell: OnceLock::new(), init: Cell::new(Some(f)) } } -} -impl T> LazyLock { /// Forces the evaluation of this lazy value and /// returns a reference to result. This is equivalent /// to the `Deref` impl, but is explicit. diff --git a/library/std/src/sync/lazy_lock/tests.rs b/library/std/src/sync/lazy_lock/tests.rs index f11b66bfca5..a5d4e25c596 100644 --- a/library/std/src/sync/lazy_lock/tests.rs +++ b/library/std/src/sync/lazy_lock/tests.rs @@ -136,6 +136,12 @@ fn sync_lazy_poisoning() { } } +// Check that we can infer `T` from closure's type. +#[test] +fn lazy_type_inference() { + let _ = LazyCell::new(|| ()); +} + #[test] fn is_sync_send() { fn assert_traits() {} diff --git a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md index e3b08648999..8ecf05f0e12 100644 --- a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md +++ b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md @@ -9,11 +9,11 @@ are the same: ```rust,no_run /// This is a doc comment. -#[doc = " This is a doc comment."] +#[doc = r" This is a doc comment."] # fn f() {} ``` -(Note the leading space in the attribute version.) +(Note the leading space and the raw string literal in the attribute version.) In most cases, `///` is easier to use than `#[doc]`. One case where the latter is easier is when generating documentation in macros; the `collapse-docs` pass will combine multiple diff --git a/src/test/ui/argument-suggestions/extern-fn-arg-names.rs b/src/test/ui/argument-suggestions/extern-fn-arg-names.rs new file mode 100644 index 00000000000..6c925a3d653 --- /dev/null +++ b/src/test/ui/argument-suggestions/extern-fn-arg-names.rs @@ -0,0 +1,9 @@ +extern "Rust" { + fn dstfn(src: i32, dst: err); + //~^ ERROR cannot find type `err` in this scope +} + +fn main() { + dstfn(1); + //~^ ERROR this function takes 2 arguments but 1 argument was supplied +} diff --git a/src/test/ui/argument-suggestions/extern-fn-arg-names.stderr b/src/test/ui/argument-suggestions/extern-fn-arg-names.stderr new file mode 100644 index 00000000000..f6bc84c1203 --- /dev/null +++ b/src/test/ui/argument-suggestions/extern-fn-arg-names.stderr @@ -0,0 +1,26 @@ +error[E0412]: cannot find type `err` in this scope + --> $DIR/extern-fn-arg-names.rs:2:29 + | +LL | fn dstfn(src: i32, dst: err); + | ^^^ not found in this scope + +error[E0061]: this function takes 2 arguments but 1 argument was supplied + --> $DIR/extern-fn-arg-names.rs:7:5 + | +LL | dstfn(1); + | ^^^^^--- an argument is missing + | +note: function defined here + --> $DIR/extern-fn-arg-names.rs:2:8 + | +LL | fn dstfn(src: i32, dst: err); + | ^^^^^ +help: provide the argument + | +LL | dstfn(1, /* dst */); + | ~~~~~~~~~~~~~~ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0061, E0412. +For more information about an error, try `rustc --explain E0061`. diff --git a/src/test/ui/rust-2018/edition-lint-infer-outlives-macro.fixed b/src/test/ui/rust-2018/edition-lint-infer-outlives-macro.fixed new file mode 100644 index 00000000000..8cdb08e81b9 --- /dev/null +++ b/src/test/ui/rust-2018/edition-lint-infer-outlives-macro.fixed @@ -0,0 +1,137 @@ +// edition:2018 +// aux-build:edition-lint-infer-outlives-macro.rs +// run-rustfix + +#![deny(explicit_outlives_requirements)] +#![allow(dead_code)] + +#[macro_use] +extern crate edition_lint_infer_outlives_macro; + +// Test that the lint does not fire if the predicate is from the local crate, +// but all the bounds are from an external macro. +macro_rules! make_foo { + ($a:tt) => { + struct Foo<$a, 'b: $a> { + foo: &$a &'b (), + } + + struct FooWhere<$a, 'b> where 'b: $a { + foo: &$a &'b (), + } + } +} + +gimme_a! {make_foo!} + +struct Bar<'a, 'b> { + //~^ ERROR: outlives requirements can be inferred + bar: &'a &'b (), +} + +struct BarWhere<'a, 'b> { + //~^ ERROR: outlives requirements can be inferred + bar: &'a &'b (), +} + +// Test that the lint *does* fire if the predicate is contained in a local macro. +mod everything_inside { + macro_rules! m { + ('b: 'a) => { + struct Foo<'a, 'b>(&'a &'b ()); + //~^ ERROR: outlives requirements can be inferred + struct Bar<'a, 'b>(&'a &'b ()) ; + //~^ ERROR: outlives requirements can be inferred + struct Baz<'a, 'b>(&'a &'b ()) where (): Sized, ; + //~^ ERROR: outlives requirements can be inferred + }; + } + m!('b: 'a); +} + +mod inner_lifetime_outside_colon_inside { + macro_rules! m { + ($b:lifetime: 'a) => { + struct Foo<'a, $b>(&'a &$b ()); + //~^ ERROR: outlives requirements can be inferred + struct Bar<'a, $b>(&'a &$b ()) ; + //~^ ERROR: outlives requirements can be inferred + struct Baz<'a, $b>(&'a &$b ()) where (): Sized, ; + //~^ ERROR: outlives requirements can be inferred + } + } + m!('b: 'a); +} + +mod outer_lifetime_outside_colon_inside { + macro_rules! m { + ('b: $a:lifetime) => { + struct Foo<$a, 'b: $a>(&$a &'b ()); + struct Bar<$a, 'b>(&$a &'b ()) where 'b: $a; + struct Baz<$a, 'b>(&$a &'b ()) where (): Sized, 'b: $a; + } + } + m!('b: 'a); +} + +mod both_lifetimes_outside_colon_inside { + macro_rules! m { + ($b:lifetime: $a:lifetime) => { + struct Foo<$a, $b: $a>(&$a &$b ()); + struct Bar<$a, $b>(&$a &$b ()) where $b: $a; + struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b: $a; + } + } + m!('b: 'a); +} + +mod everything_outside { + macro_rules! m { + ($b:lifetime $colon:tt $a:lifetime) => { + struct Foo<$a, $b $colon $a>(&$a &$b ()); + struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a; + struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a; + } + } + m!('b: 'a); +} + +mod everything_outside_with_tt_inner { + macro_rules! m { + ($b:tt $colon:tt $a:lifetime) => { + struct Foo<$a, $b $colon $a>(&$a &$b ()); + struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a; + struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a; + } + } + m!('b: 'a); +} + +// FIXME: These should be consistent. +mod everything_outside_with_tt_outer { + macro_rules! m { + ($b:lifetime $colon:tt $a:tt) => { + struct Foo<$a, $b >(&$a &$b ()); + //~^ ERROR: outlives requirements can be inferred + struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a; + struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a; + } + } + m!('b: 'a); +} + +mod everything_outside_with_tt_both { + macro_rules! m { + ($b:tt $colon:tt $a:tt) => { + struct Foo<$a, $b >(&$a &$b ()); + //~^ ERROR: outlives requirements can be inferred + struct Bar<$a, $b>(&$a &$b ()) where ; + //~^ ERROR: outlives requirements can be inferred + struct Baz<$a, $b>(&$a &$b ()) where (): Sized, ; + //~^ ERROR: outlives requirements can be inferred + } + } + m!('b: 'a); +} + +fn main() {} diff --git a/src/test/ui/rust-2018/edition-lint-infer-outlives-macro.rs b/src/test/ui/rust-2018/edition-lint-infer-outlives-macro.rs index d7a832831c1..647906c2dc2 100644 --- a/src/test/ui/rust-2018/edition-lint-infer-outlives-macro.rs +++ b/src/test/ui/rust-2018/edition-lint-infer-outlives-macro.rs @@ -1,18 +1,22 @@ // edition:2018 // aux-build:edition-lint-infer-outlives-macro.rs - -// Test that the lint does not fire if the where predicate -// is from the local crate, but all the bounds are from an -// external macro. +// run-rustfix #![deny(explicit_outlives_requirements)] +#![allow(dead_code)] #[macro_use] extern crate edition_lint_infer_outlives_macro; +// Test that the lint does not fire if the predicate is from the local crate, +// but all the bounds are from an external macro. macro_rules! make_foo { ($a:tt) => { - struct Foo<$a, 'b> where 'b: $a { + struct Foo<$a, 'b: $a> { + foo: &$a &'b (), + } + + struct FooWhere<$a, 'b> where 'b: $a { foo: &$a &'b (), } } @@ -25,4 +29,109 @@ struct Bar<'a, 'b: 'a> { bar: &'a &'b (), } +struct BarWhere<'a, 'b> where 'b: 'a { + //~^ ERROR: outlives requirements can be inferred + bar: &'a &'b (), +} + +// Test that the lint *does* fire if the predicate is contained in a local macro. +mod everything_inside { + macro_rules! m { + ('b: 'a) => { + struct Foo<'a, 'b: 'a>(&'a &'b ()); + //~^ ERROR: outlives requirements can be inferred + struct Bar<'a, 'b>(&'a &'b ()) where 'b: 'a; + //~^ ERROR: outlives requirements can be inferred + struct Baz<'a, 'b>(&'a &'b ()) where (): Sized, 'b: 'a; + //~^ ERROR: outlives requirements can be inferred + }; + } + m!('b: 'a); +} + +mod inner_lifetime_outside_colon_inside { + macro_rules! m { + ($b:lifetime: 'a) => { + struct Foo<'a, $b: 'a>(&'a &$b ()); + //~^ ERROR: outlives requirements can be inferred + struct Bar<'a, $b>(&'a &$b ()) where $b: 'a; + //~^ ERROR: outlives requirements can be inferred + struct Baz<'a, $b>(&'a &$b ()) where (): Sized, $b: 'a; + //~^ ERROR: outlives requirements can be inferred + } + } + m!('b: 'a); +} + +mod outer_lifetime_outside_colon_inside { + macro_rules! m { + ('b: $a:lifetime) => { + struct Foo<$a, 'b: $a>(&$a &'b ()); + struct Bar<$a, 'b>(&$a &'b ()) where 'b: $a; + struct Baz<$a, 'b>(&$a &'b ()) where (): Sized, 'b: $a; + } + } + m!('b: 'a); +} + +mod both_lifetimes_outside_colon_inside { + macro_rules! m { + ($b:lifetime: $a:lifetime) => { + struct Foo<$a, $b: $a>(&$a &$b ()); + struct Bar<$a, $b>(&$a &$b ()) where $b: $a; + struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b: $a; + } + } + m!('b: 'a); +} + +mod everything_outside { + macro_rules! m { + ($b:lifetime $colon:tt $a:lifetime) => { + struct Foo<$a, $b $colon $a>(&$a &$b ()); + struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a; + struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a; + } + } + m!('b: 'a); +} + +mod everything_outside_with_tt_inner { + macro_rules! m { + ($b:tt $colon:tt $a:lifetime) => { + struct Foo<$a, $b $colon $a>(&$a &$b ()); + struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a; + struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a; + } + } + m!('b: 'a); +} + +// FIXME: These should be consistent. +mod everything_outside_with_tt_outer { + macro_rules! m { + ($b:lifetime $colon:tt $a:tt) => { + struct Foo<$a, $b $colon $a>(&$a &$b ()); + //~^ ERROR: outlives requirements can be inferred + struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a; + struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a; + } + } + m!('b: 'a); +} + +mod everything_outside_with_tt_both { + macro_rules! m { + ($b:tt $colon:tt $a:tt) => { + struct Foo<$a, $b $colon $a>(&$a &$b ()); + //~^ ERROR: outlives requirements can be inferred + struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a; + //~^ ERROR: outlives requirements can be inferred + struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a; + //~^ ERROR: outlives requirements can be inferred + } + } + m!('b: 'a); +} + fn main() {} diff --git a/src/test/ui/rust-2018/edition-lint-infer-outlives-macro.stderr b/src/test/ui/rust-2018/edition-lint-infer-outlives-macro.stderr index 553b1cd976a..734ae687978 100644 --- a/src/test/ui/rust-2018/edition-lint-infer-outlives-macro.stderr +++ b/src/test/ui/rust-2018/edition-lint-infer-outlives-macro.stderr @@ -1,14 +1,110 @@ error: outlives requirements can be inferred - --> $DIR/edition-lint-infer-outlives-macro.rs:23:18 + --> $DIR/edition-lint-infer-outlives-macro.rs:27:18 | LL | struct Bar<'a, 'b: 'a> { | ^^^^ help: remove this bound | note: the lint level is defined here - --> $DIR/edition-lint-infer-outlives-macro.rs:8:9 + --> $DIR/edition-lint-infer-outlives-macro.rs:5:9 | LL | #![deny(explicit_outlives_requirements)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-macro.rs:32:24 + | +LL | struct BarWhere<'a, 'b> where 'b: 'a { + | ^^^^^^^^^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-macro.rs:41:30 + | +LL | struct Foo<'a, 'b: 'a>(&'a &'b ()); + | ^^^^ help: remove this bound +... +LL | m!('b: 'a); + | ---------- in this macro invocation + | + = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-macro.rs:43:44 + | +LL | struct Bar<'a, 'b>(&'a &'b ()) where 'b: 'a; + | ^^^^^^^^^^^^ help: remove this bound +... +LL | m!('b: 'a); + | ---------- in this macro invocation + | + = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-macro.rs:45:61 + | +LL | struct Baz<'a, 'b>(&'a &'b ()) where (): Sized, 'b: 'a; + | ^^^^^^ help: remove this bound +... +LL | m!('b: 'a); + | ---------- in this macro invocation + | + = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-macro.rs:55:30 + | +LL | struct Foo<'a, $b: 'a>(&'a &$b ()); + | ^^^^ help: remove this bound +... +LL | m!('b: 'a); + | ---------- in this macro invocation + | + = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-macro.rs:57:44 + | +LL | struct Bar<'a, $b>(&'a &$b ()) where $b: 'a; + | ^^^^^^^^^^^^ help: remove this bound +... +LL | m!('b: 'a); + | ---------- in this macro invocation + | + = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-macro.rs:59:61 + | +LL | struct Baz<'a, $b>(&'a &$b ()) where (): Sized, $b: 'a; + | ^^^^^^ help: remove this bound +... +LL | m!('b: 'a); + | ---------- in this macro invocation + | + = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-macro.rs:114:31 + | +LL | struct Foo<$a, $b $colon $a>(&$a &$b ()); + | ^^^^^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-macro.rs:126:31 + | +LL | struct Foo<$a, $b $colon $a>(&$a &$b ()); + | ^^^^^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-macro.rs:128:50 + | +LL | struct Bar<$a, $b>(&$a &$b ()) where $b $colon $a; + | ^^^^^^^^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-macro.rs:130:61 + | +LL | struct Baz<$a, $b>(&$a &$b ()) where (): Sized, $b $colon $a; + | ^^^^^^^^^^^^ help: remove this bound + +error: aborting due to 12 previous errors diff --git a/src/test/ui/suggestions/bad-hex-float-lit.rs b/src/test/ui/suggestions/bad-hex-float-lit.rs new file mode 100644 index 00000000000..cd6fdbde96c --- /dev/null +++ b/src/test/ui/suggestions/bad-hex-float-lit.rs @@ -0,0 +1,13 @@ +fn main() { + let _f: f32 = 0xAAf32; + //~^ ERROR mismatched types + //~| HELP rewrite this + + let _f: f32 = 0xAB_f32; + //~^ ERROR mismatched types + //~| HELP rewrite this + + let _f: f64 = 0xFF_f64; + //~^ ERROR mismatched types + //~| HELP rewrite this +} diff --git a/src/test/ui/suggestions/bad-hex-float-lit.stderr b/src/test/ui/suggestions/bad-hex-float-lit.stderr new file mode 100644 index 00000000000..bc09abb1a56 --- /dev/null +++ b/src/test/ui/suggestions/bad-hex-float-lit.stderr @@ -0,0 +1,48 @@ +error[E0308]: mismatched types + --> $DIR/bad-hex-float-lit.rs:2:19 + | +LL | let _f: f32 = 0xAAf32; + | --- ^^^^^^^ expected `f32`, found integer + | | + | expected due to this + | +help: rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float + | +LL | let _f: f32 = 0xAA as f32; + | ~~~~~~~~~~~ +LL | let _f: f32 = 170_f32; + | ~~~~~~~ + +error[E0308]: mismatched types + --> $DIR/bad-hex-float-lit.rs:6:19 + | +LL | let _f: f32 = 0xAB_f32; + | --- ^^^^^^^^ expected `f32`, found integer + | | + | expected due to this + | +help: rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float + | +LL | let _f: f32 = 0xAB as f32; + | ~~~~~~~~~~~ +LL | let _f: f32 = 171_f32; + | ~~~~~~~ + +error[E0308]: mismatched types + --> $DIR/bad-hex-float-lit.rs:10:19 + | +LL | let _f: f64 = 0xFF_f64; + | --- ^^^^^^^^ expected `f64`, found integer + | | + | expected due to this + | +help: rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float + | +LL | let _f: f64 = 0xFF as f64; + | ~~~~~~~~~~~ +LL | let _f: f64 = 255_f64; + | ~~~~~~~ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/method-access-to-range-literal-typo.rs b/src/test/ui/suggestions/method-access-to-range-literal-typo.rs new file mode 100644 index 00000000000..ac662edafe6 --- /dev/null +++ b/src/test/ui/suggestions/method-access-to-range-literal-typo.rs @@ -0,0 +1,30 @@ +fn as_ref() -> Option> { + None +} +struct Type { + option: Option> +} +trait Trait { + fn foo(&self) -> Vec; +} +impl Trait for Option> { + fn foo(&self) -> Vec { + vec![1, 2, 3] + } +} + +impl Type { + fn method(&self) -> Option> { + self.option..as_ref().map(|x| x) + //~^ ERROR E0308 + } + fn method2(&self) -> &u8 { + self.option..foo().get(0) + //~^ ERROR E0425 + //~| ERROR E0308 + } +} + +fn main() { + let _ = Type { option: None }.method(); +} diff --git a/src/test/ui/suggestions/method-access-to-range-literal-typo.stderr b/src/test/ui/suggestions/method-access-to-range-literal-typo.stderr new file mode 100644 index 00000000000..c84f9467891 --- /dev/null +++ b/src/test/ui/suggestions/method-access-to-range-literal-typo.stderr @@ -0,0 +1,48 @@ +error[E0425]: cannot find function `foo` in this scope + --> $DIR/method-access-to-range-literal-typo.rs:22:22 + | +LL | self.option..foo().get(0) + | ^^^ not found in this scope + | +help: you might have meant to write `.` instead of `..` + | +LL - self.option..foo().get(0) +LL + self.option.foo().get(0) + | + +error[E0308]: mismatched types + --> $DIR/method-access-to-range-literal-typo.rs:18:9 + | +LL | fn method(&self) -> Option> { + | --------------- expected `Option>` because of return type +LL | self.option..as_ref().map(|x| x) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found struct `Range` + | + = note: expected enum `Option<_>` + found struct `std::ops::Range>` +help: you likely meant to write a method call instead of a range + | +LL - self.option..as_ref().map(|x| x) +LL + self.option.as_ref().map(|x| x) + | + +error[E0308]: mismatched types + --> $DIR/method-access-to-range-literal-typo.rs:22:9 + | +LL | fn method2(&self) -> &u8 { + | --- expected `&u8` because of return type +LL | self.option..foo().get(0) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found struct `Range` + | + = note: expected reference `&u8` + found struct `std::ops::Range>>` +help: you likely meant to write a method call instead of a range + | +LL - self.option..foo().get(0) +LL + self.option.foo().get(0) + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0425. +For more information about an error, try `rustc --explain E0308`.