diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl index 6d34cdce3408f..13c368d1c5850 100644 --- a/compiler/rustc_error_messages/locales/en-US/parser.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl @@ -245,7 +245,7 @@ parser_assignment_else_not_allowed = ... else {"{"} ... {"}"} is no parser_expected_statement_after_outer_attr = expected statement after outer attribute parser_doc_comment_does_not_document_anything = found a documentation comment that doesn't document anything - .help = doc comments must come before what they document, maybe a comment was intended with `//`? + .help = doc comments must come before what they document, if a comment was intended use `//` .suggestion = missing comma here parser_const_let_mutually_exclusive = `const` and `let` are mutually exclusive diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 70efa748846ab..f69ac0768204a 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2173,10 +2173,16 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { let mut region_index = self.region_index; let mut next_name = |this: &Self| { - let name = name_by_region_index(region_index, &mut available_names, num_available); - debug!(?name); - region_index += 1; - assert!(!this.used_region_names.contains(&name)); + let mut name; + + loop { + name = name_by_region_index(region_index, &mut available_names, num_available); + region_index += 1; + + if !this.used_region_names.contains(&name) { + break; + } + } name }; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index b7454d7bfc18e..c8a8e00b1fa03 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -760,8 +760,8 @@ impl<'a> Parser<'a> { ) .span_label(self.token.span, "this doc comment doesn't document anything") .help( - "doc comments must come before what they document, maybe a \ - comment was intended with `//`?", + "doc comments must come before what they document, if a comment was \ + intended use `//`", ) .emit(); self.bump(); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 3c276a9ada97b..824cebd7e0a5b 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -130,6 +130,16 @@ pub(super) enum LifetimeElisionCandidate { Missing(MissingLifetime), } +/// Only used for diagnostics. +struct BaseError { + msg: String, + fallback_label: String, + span: Span, + span_label: Option<(Span, &'static str)>, + could_be_expr: bool, + suggestion: Option<(Span, &'static str, String)>, +} + impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn def_span(&self, def_id: DefId) -> Option { match def_id.krate { @@ -138,35 +148,18 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } - /// Handles error reporting for `smart_resolve_path_fragment` function. - /// Creates base error and amends it with one short label and possibly some longer helps/notes. - pub(crate) fn smart_resolve_report_errors( + fn make_base_error( &mut self, path: &[Segment], span: Span, source: PathSource<'_>, res: Option, - ) -> (DiagnosticBuilder<'a, ErrorGuaranteed>, Vec) { - let ident_span = path.last().map_or(span, |ident| ident.ident.span); - let ns = source.namespace(); - let is_expected = &|res| source.is_expected(res); - let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); - - debug!(?res, ?source); - + ) -> BaseError { // Make the base error. - struct BaseError<'a> { - msg: String, - fallback_label: String, - span: Span, - span_label: Option<(Span, &'a str)>, - could_be_expr: bool, - suggestion: Option<(Span, &'a str, String)>, - } let mut expected = source.descr_expected(); let path_str = Segment::names_to_string(path); let item_str = path.last().unwrap().ident; - let base_error = if let Some(res) = res { + if let Some(res) = res { BaseError { msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str), fallback_label: format!("not a {expected}"), @@ -277,8 +270,20 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { could_be_expr: false, suggestion, } - }; + } + } + /// Handles error reporting for `smart_resolve_path_fragment` function. + /// Creates base error and amends it with one short label and possibly some longer helps/notes. + pub(crate) fn smart_resolve_report_errors( + &mut self, + path: &[Segment], + span: Span, + source: PathSource<'_>, + res: Option, + ) -> (DiagnosticBuilder<'a, ErrorGuaranteed>, Vec) { + debug!(?res, ?source); + let base_error = self.make_base_error(path, span, source, res); let code = source.error_code(res.is_some()); let mut err = self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code); @@ -289,41 +294,79 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_label(span, label); } - if let Some(sugg) = base_error.suggestion { - err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect); + if let Some(ref sugg) = base_error.suggestion { + err.span_suggestion_verbose(sugg.0, sugg.1, &sugg.2, Applicability::MaybeIncorrect); } - if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal { - err.multipart_suggestion( - "you might have meant to write a `struct` literal", - vec![ - (span.shrink_to_lo(), "{ SomeStruct ".to_string()), - (span.shrink_to_hi(), "}".to_string()), - ], - Applicability::HasPlaceholders, - ); + self.suggest_bare_struct_literal(&mut err); + self.suggest_pattern_match_with_let(&mut err, source, span); + + self.suggest_self_or_self_ref(&mut err, path, span); + self.detect_assoct_type_constraint_meant_as_path(&mut err, &base_error); + if self.suggest_self_ty(&mut err, source, path, span) + || self.suggest_self_value(&mut err, source, path, span) + { + return (err, Vec::new()); } - match (source, self.diagnostic_metadata.in_if_condition) { - ( - PathSource::Expr(_), - Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. }), - ) => { - // Icky heuristic so we don't suggest: - // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern) - // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span) - if lhs.is_approximately_pattern() && lhs.span.contains(span) { - err.span_suggestion_verbose( - expr_span.shrink_to_lo(), - "you might have meant to use pattern matching", - "let ", - Applicability::MaybeIncorrect, - ); + + let (found, candidates) = + self.try_lookup_name_relaxed(&mut err, source, path, span, res, &base_error); + if found { + return (err, candidates); + } + + if !self.type_ascription_suggestion(&mut err, base_error.span) { + let mut fallback = + self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error); + fallback |= self.suggest_typo(&mut err, source, path, span, &base_error); + if fallback { + // Fallback label. + err.span_label(base_error.span, &base_error.fallback_label); + } + } + self.err_code_special_cases(&mut err, source, path, span); + + (err, candidates) + } + + fn detect_assoct_type_constraint_meant_as_path( + &self, + err: &mut Diagnostic, + base_error: &BaseError, + ) { + let Some(ty) = self.diagnostic_metadata.current_type_path else { return; }; + let TyKind::Path(_, path) = &ty.kind else { return; }; + for segment in &path.segments { + let Some(params) = &segment.args else { continue; }; + let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else { continue; }; + for param in ¶ms.args { + let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; }; + let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else { + continue; + }; + for bound in bounds { + let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None) + = bound else + { + continue; + }; + if base_error.span == trait_ref.span { + err.span_suggestion_verbose( + constraint.ident.span.between(trait_ref.span), + "you might have meant to write a path instead of an associated type bound", + "::", + Applicability::MachineApplicable, + ); + } } } - _ => {} } + } + fn suggest_self_or_self_ref(&mut self, err: &mut Diagnostic, path: &[Segment], span: Span) { let is_assoc_fn = self.self_type_is_available(); + let Some(path_last_segment) = path.last() else { return }; + let item_str = path_last_segment.ident; // Emit help message for fake-self from other languages (e.g., `this` in Javascript). if ["this", "my"].contains(&item_str.as_str()) && is_assoc_fn { err.span_suggestion_short( @@ -358,96 +401,25 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } } + } - self.detect_assoct_type_constraint_meant_as_path(base_error.span, &mut err); - - // Emit special messages for unresolved `Self` and `self`. - if is_self_type(path, ns) { - err.code(rustc_errors::error_code!(E0411)); - err.span_label( - span, - "`Self` is only available in impls, traits, and type definitions".to_string(), - ); - if let Some(item_kind) = self.diagnostic_metadata.current_item { - err.span_label( - item_kind.ident.span, - format!( - "`Self` not allowed in {} {}", - item_kind.kind.article(), - item_kind.kind.descr() - ), - ); - } - return (err, Vec::new()); - } - if is_self_value(path, ns) { - debug!("smart_resolve_path_fragment: E0424, source={:?}", source); - - err.code(rustc_errors::error_code!(E0424)); - err.span_label(span, match source { - PathSource::Pat => "`self` value is a keyword and may not be bound to variables or shadowed", - _ => "`self` value is a keyword only available in methods with a `self` parameter", - }); - if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function { - // The current function has a `self' parameter, but we were unable to resolve - // a reference to `self`. This can only happen if the `self` identifier we - // are resolving came from a different hygiene context. - if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) { - err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); - } else { - let doesnt = if is_assoc_fn { - let (span, sugg) = fn_kind - .decl() - .inputs - .get(0) - .map(|p| (p.span.shrink_to_lo(), "&self, ")) - .unwrap_or_else(|| { - // Try to look for the "(" after the function name, if possible. - // This avoids placing the suggestion into the visibility specifier. - let span = fn_kind - .ident() - .map_or(*span, |ident| span.with_lo(ident.span.hi())); - ( - self.r - .session - .source_map() - .span_through_char(span, '(') - .shrink_to_hi(), - "&self", - ) - }); - err.span_suggestion_verbose( - span, - "add a `self` receiver parameter to make the associated `fn` a method", - sugg, - Applicability::MaybeIncorrect, - ); - "doesn't" - } else { - "can't" - }; - if let Some(ident) = fn_kind.ident() { - err.span_label( - ident.span, - &format!("this function {} have a `self` parameter", doesnt), - ); - } - } - } else if let Some(item_kind) = self.diagnostic_metadata.current_item { - err.span_label( - item_kind.ident.span, - format!( - "`self` not allowed in {} {}", - item_kind.kind.article(), - item_kind.kind.descr() - ), - ); - } - return (err, Vec::new()); - } - + fn try_lookup_name_relaxed( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + source: PathSource<'_>, + path: &[Segment], + span: Span, + res: Option, + base_error: &BaseError, + ) -> (bool, Vec) { // Try to lookup name in more relaxed fashion for better error reporting. let ident = path.last().unwrap().ident; + let is_expected = &|res| source.is_expected(res); + let ns = source.namespace(); + let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); + let path_str = Segment::names_to_string(path); + let ident_span = path.last().map_or(span, |ident| ident.ident.span); + let mut candidates = self .r .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected) @@ -494,7 +466,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { { // Already reported this issue on the lhs of the type ascription. err.delay_as_bug(); - return (err, candidates); + return (true, candidates); } } @@ -522,8 +494,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ); } } + // Try Levenshtein algorithm. - let typo_sugg = self.lookup_typo_candidate(path, ns, is_expected); + let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected); if path.len() == 1 && self.self_type_is_available() { if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) { let self_is_available = self.self_value_is_available(path[0].ident.span); @@ -560,8 +533,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ); } } - self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span); - return (err, candidates); + self.r.add_typo_suggestion(err, typo_sugg, ident_span); + return (true, candidates); } // If the first argument in call is `self` suggest calling a method. @@ -579,14 +552,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { format!("self.{path_str}({args_snippet})"), Applicability::MachineApplicable, ); - return (err, candidates); + return (true, candidates); } } // Try context-dependent help if relaxed lookup didn't work. if let Some(res) = res { if self.smart_resolve_context_dependent_help( - &mut err, + err, span, source, res, @@ -594,106 +567,135 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { &base_error.fallback_label, ) { // We do this to avoid losing a secondary span when we override the main error span. - self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span); - return (err, candidates); + self.r.add_typo_suggestion(err, typo_sugg, ident_span); + return (true, candidates); } } + return (false, candidates); + } + fn suggest_trait_and_bounds( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + source: PathSource<'_>, + res: Option, + span: Span, + base_error: &BaseError, + ) -> bool { let is_macro = base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none(); - if !self.type_ascription_suggestion(&mut err, base_error.span) { - let mut fallback = false; - if let ( - PathSource::Trait(AliasPossibility::Maybe), - Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)), - false, - ) = (source, res, is_macro) - { - if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object { - fallback = true; - let spans: Vec = bounds - .iter() - .map(|bound| bound.span()) - .filter(|&sp| sp != base_error.span) - .collect(); + let mut fallback = false; - let start_span = bounds[0].span(); - // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) - let end_span = bounds.last().unwrap().span(); - // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) - let last_bound_span = spans.last().cloned().unwrap(); - let mut multi_span: MultiSpan = spans.clone().into(); - for sp in spans { - let msg = if sp == last_bound_span { - format!( - "...because of {these} bound{s}", - these = pluralize!("this", bounds.len() - 1), - s = pluralize!(bounds.len() - 1), - ) - } else { - String::new() - }; - multi_span.push_span_label(sp, msg); - } - multi_span - .push_span_label(base_error.span, "expected this type to be a trait..."); - err.span_help( - multi_span, - "`+` is used to constrain a \"trait object\" type with lifetimes or \ - auto-traits; structs and enums can't be bound in that way", - ); - if bounds.iter().all(|bound| match bound { - ast::GenericBound::Outlives(_) => true, - ast::GenericBound::Trait(tr, _) => tr.span == base_error.span, - }) { - let mut sugg = vec![]; - if base_error.span != start_span { - sugg.push((start_span.until(base_error.span), String::new())); - } - if base_error.span != end_span { - sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new())); - } + if let ( + PathSource::Trait(AliasPossibility::Maybe), + Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)), + false, + ) = (source, res, is_macro) + { + if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object { + fallback = true; + let spans: Vec = bounds + .iter() + .map(|bound| bound.span()) + .filter(|&sp| sp != base_error.span) + .collect(); - err.multipart_suggestion( - "if you meant to use a type and not a trait here, remove the bounds", - sugg, - Applicability::MaybeIncorrect, - ); + let start_span = bounds[0].span(); + // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) + let end_span = bounds.last().unwrap().span(); + // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) + let last_bound_span = spans.last().cloned().unwrap(); + let mut multi_span: MultiSpan = spans.clone().into(); + for sp in spans { + let msg = if sp == last_bound_span { + format!( + "...because of {these} bound{s}", + these = pluralize!("this", bounds.len() - 1), + s = pluralize!(bounds.len() - 1), + ) + } else { + String::new() + }; + multi_span.push_span_label(sp, msg); + } + multi_span.push_span_label(base_error.span, "expected this type to be a trait..."); + err.span_help( + multi_span, + "`+` is used to constrain a \"trait object\" type with lifetimes or \ + auto-traits; structs and enums can't be bound in that way", + ); + if bounds.iter().all(|bound| match bound { + ast::GenericBound::Outlives(_) => true, + ast::GenericBound::Trait(tr, _) => tr.span == base_error.span, + }) { + let mut sugg = vec![]; + if base_error.span != start_span { + sugg.push((start_span.until(base_error.span), String::new())); } + if base_error.span != end_span { + sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new())); + } + + err.multipart_suggestion( + "if you meant to use a type and not a trait here, remove the bounds", + sugg, + Applicability::MaybeIncorrect, + ); } } + } - fallback |= self.restrict_assoc_type_in_where_clause(span, &mut err); + fallback |= self.restrict_assoc_type_in_where_clause(span, err); + fallback + } - if !self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span) { - fallback = true; - match self.diagnostic_metadata.current_let_binding { - Some((pat_sp, Some(ty_sp), None)) - if ty_sp.contains(base_error.span) && base_error.could_be_expr => - { - err.span_suggestion_short( - pat_sp.between(ty_sp), - "use `=` if you meant to assign", - " = ", - Applicability::MaybeIncorrect, - ); - } - _ => {} + fn suggest_typo( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + source: PathSource<'_>, + path: &[Segment], + span: Span, + base_error: &BaseError, + ) -> bool { + let is_expected = &|res| source.is_expected(res); + let ident_span = path.last().map_or(span, |ident| ident.ident.span); + let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected); + let mut fallback = false; + if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) { + fallback = true; + match self.diagnostic_metadata.current_let_binding { + Some((pat_sp, Some(ty_sp), None)) + if ty_sp.contains(base_error.span) && base_error.could_be_expr => + { + err.span_suggestion_short( + pat_sp.between(ty_sp), + "use `=` if you meant to assign", + " = ", + Applicability::MaybeIncorrect, + ); } - - // If the trait has a single item (which wasn't matched by Levenshtein), suggest it - let suggestion = self.get_single_associated_item(&path, &source, is_expected); - self.r.add_typo_suggestion(&mut err, suggestion, ident_span); - } - if fallback { - // Fallback label. - err.span_label(base_error.span, base_error.fallback_label); + _ => {} } + + // If the trait has a single item (which wasn't matched by Levenshtein), suggest it + let suggestion = self.get_single_associated_item(&path, &source, is_expected); + self.r.add_typo_suggestion(err, suggestion, ident_span); } + fallback + } + + fn err_code_special_cases( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + source: PathSource<'_>, + path: &[Segment], + span: Span, + ) { if let Some(err_code) = &err.code { if err_code == &rustc_errors::error_code!(E0425) { for label_rib in &self.label_ribs { for (label_ident, node_id) in &label_rib.bindings { + let ident = path.last().unwrap().ident; if format!("'{}", ident) == label_ident.to_string() { err.span_label(label_ident.span, "a label with a similar name exists"); if let PathSource::Expr(Some(Expr { @@ -724,38 +726,116 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } } + } - (err, candidates) + /// Emit special messages for unresolved `Self` and `self`. + fn suggest_self_ty( + &mut self, + err: &mut Diagnostic, + source: PathSource<'_>, + path: &[Segment], + span: Span, + ) -> bool { + if !is_self_type(path, source.namespace()) { + return false; + } + err.code(rustc_errors::error_code!(E0411)); + err.span_label( + span, + "`Self` is only available in impls, traits, and type definitions".to_string(), + ); + if let Some(item_kind) = self.diagnostic_metadata.current_item { + err.span_label( + item_kind.ident.span, + format!( + "`Self` not allowed in {} {}", + item_kind.kind.article(), + item_kind.kind.descr() + ), + ); + } + true } - fn detect_assoct_type_constraint_meant_as_path(&self, base_span: Span, err: &mut Diagnostic) { - let Some(ty) = self.diagnostic_metadata.current_type_path else { return; }; - let TyKind::Path(_, path) = &ty.kind else { return; }; - for segment in &path.segments { - let Some(params) = &segment.args else { continue; }; - let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else { continue; }; - for param in ¶ms.args { - let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; }; - let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else { - continue; + fn suggest_self_value( + &mut self, + err: &mut Diagnostic, + source: PathSource<'_>, + path: &[Segment], + span: Span, + ) -> bool { + if !is_self_value(path, source.namespace()) { + return false; + } + + debug!("smart_resolve_path_fragment: E0424, source={:?}", source); + err.code(rustc_errors::error_code!(E0424)); + err.span_label( + span, + match source { + PathSource::Pat => { + "`self` value is a keyword and may not be bound to variables or shadowed" + } + _ => "`self` value is a keyword only available in methods with a `self` parameter", + }, + ); + let is_assoc_fn = self.self_type_is_available(); + if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function { + // The current function has a `self' parameter, but we were unable to resolve + // a reference to `self`. This can only happen if the `self` identifier we + // are resolving came from a different hygiene context. + if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) { + err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); + } else { + let doesnt = if is_assoc_fn { + let (span, sugg) = fn_kind + .decl() + .inputs + .get(0) + .map(|p| (p.span.shrink_to_lo(), "&self, ")) + .unwrap_or_else(|| { + // Try to look for the "(" after the function name, if possible. + // This avoids placing the suggestion into the visibility specifier. + let span = fn_kind + .ident() + .map_or(*span, |ident| span.with_lo(ident.span.hi())); + ( + self.r + .session + .source_map() + .span_through_char(span, '(') + .shrink_to_hi(), + "&self", + ) + }); + err.span_suggestion_verbose( + span, + "add a `self` receiver parameter to make the associated `fn` a method", + sugg, + Applicability::MaybeIncorrect, + ); + "doesn't" + } else { + "can't" }; - for bound in bounds { - let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None) - = bound else - { - continue; - }; - if base_span == trait_ref.span { - err.span_suggestion_verbose( - constraint.ident.span.between(trait_ref.span), - "you might have meant to write a path instead of an associated type bound", - "::", - Applicability::MachineApplicable, - ); - } + if let Some(ident) = fn_kind.ident() { + err.span_label( + ident.span, + &format!("this function {} have a `self` parameter", doesnt), + ); } } + } else if let Some(item_kind) = self.diagnostic_metadata.current_item { + err.span_label( + item_kind.ident.span, + format!( + "`self` not allowed in {} {}", + item_kind.kind.article(), + item_kind.kind.descr() + ), + ); } + true } fn suggest_swapping_misplaced_self_ty_and_trait( @@ -787,6 +867,45 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } + fn suggest_bare_struct_literal(&mut self, err: &mut Diagnostic) { + if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal { + err.multipart_suggestion( + "you might have meant to write a `struct` literal", + vec![ + (span.shrink_to_lo(), "{ SomeStruct ".to_string()), + (span.shrink_to_hi(), "}".to_string()), + ], + Applicability::HasPlaceholders, + ); + } + } + + fn suggest_pattern_match_with_let( + &mut self, + err: &mut Diagnostic, + source: PathSource<'_>, + span: Span, + ) { + if let PathSource::Expr(_) = source && + let Some(Expr { + span: expr_span, + kind: ExprKind::Assign(lhs, _, _), + .. + }) = self.diagnostic_metadata.in_if_condition { + // Icky heuristic so we don't suggest: + // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern) + // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span) + if lhs.is_approximately_pattern() && lhs.span.contains(span) { + err.span_suggestion_verbose( + expr_span.shrink_to_lo(), + "you might have meant to use pattern matching", + "let ", + Applicability::MaybeIncorrect, + ); + } + } + } + fn get_single_associated_item( &mut self, path: &[Segment], diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 5e32a27cdb1da..0ed59223337a5 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1737,12 +1737,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => { // See if we can toss out `victim` based on specialization. - // This requires us to know *for sure* that the `other` impl applies - // i.e., `EvaluatedToOk`. + // While this requires us to know *for sure* that the `other` impl applies + // we still use modulo regions here. // - // FIXME(@lcnr): Using `modulo_regions` here seems kind of scary - // to me but is required for `std` to compile, so I didn't change it - // for now. + // This is fine as specialization currently assumes that specializing + // impls have to be always applicable, meaning that the only allowed + // region constraints may be constraints also present on the default impl. let tcx = self.tcx(); if other.evaluation.must_apply_modulo_regions() { if tcx.specializes((other_def, victim_def)) { diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 4f29ecc0fba8b..2b85d6e22258f 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -29,6 +29,20 @@ const NANOS_PER_MICRO: u32 = 1_000; const MILLIS_PER_SEC: u64 = 1_000; const MICROS_PER_SEC: u64 = 1_000_000; +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +#[rustc_layout_scalar_valid_range_end(999_999_999)] +struct Nanoseconds(u32); + +impl Default for Nanoseconds { + #[inline] + fn default() -> Self { + // SAFETY: 0 is within the valid range + unsafe { Nanoseconds(0) } + } +} + /// A `Duration` type to represent a span of time, typically used for system /// timeouts. /// @@ -71,7 +85,7 @@ const MICROS_PER_SEC: u64 = 1_000_000; #[cfg_attr(not(test), rustc_diagnostic_item = "Duration")] pub struct Duration { secs: u64, - nanos: u32, // Always 0 <= nanos < NANOS_PER_SEC + nanos: Nanoseconds, // Always 0 <= nanos < NANOS_PER_SEC } impl Duration { @@ -188,7 +202,8 @@ impl Duration { None => panic!("overflow in Duration::new"), }; let nanos = nanos % NANOS_PER_SEC; - Duration { secs, nanos } + // SAFETY: nanos % NANOS_PER_SEC < NANOS_PER_SEC, therefore nanos is within the valid range + Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } } /// Creates a new `Duration` from the specified number of whole seconds. @@ -208,7 +223,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] pub const fn from_secs(secs: u64) -> Duration { - Duration { secs, nanos: 0 } + Duration::new(secs, 0) } /// Creates a new `Duration` from the specified number of milliseconds. @@ -228,10 +243,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] pub const fn from_millis(millis: u64) -> Duration { - Duration { - secs: millis / MILLIS_PER_SEC, - nanos: ((millis % MILLIS_PER_SEC) as u32) * NANOS_PER_MILLI, - } + Duration::new(millis / MILLIS_PER_SEC, ((millis % MILLIS_PER_SEC) as u32) * NANOS_PER_MILLI) } /// Creates a new `Duration` from the specified number of microseconds. @@ -251,10 +263,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] pub const fn from_micros(micros: u64) -> Duration { - Duration { - secs: micros / MICROS_PER_SEC, - nanos: ((micros % MICROS_PER_SEC) as u32) * NANOS_PER_MICRO, - } + Duration::new(micros / MICROS_PER_SEC, ((micros % MICROS_PER_SEC) as u32) * NANOS_PER_MICRO) } /// Creates a new `Duration` from the specified number of nanoseconds. @@ -274,10 +283,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] pub const fn from_nanos(nanos: u64) -> Duration { - Duration { - secs: nanos / (NANOS_PER_SEC as u64), - nanos: (nanos % (NANOS_PER_SEC as u64)) as u32, - } + Duration::new(nanos / (NANOS_PER_SEC as u64), (nanos % (NANOS_PER_SEC as u64)) as u32) } /// Returns true if this `Duration` spans no time. @@ -301,7 +307,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_zero", since = "1.53.0")] #[inline] pub const fn is_zero(&self) -> bool { - self.secs == 0 && self.nanos == 0 + self.secs == 0 && self.nanos.0 == 0 } /// Returns the number of _whole_ seconds contained by this `Duration`. @@ -352,7 +358,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_millis(&self) -> u32 { - self.nanos / NANOS_PER_MILLI + self.nanos.0 / NANOS_PER_MILLI } /// Returns the fractional part of this `Duration`, in whole microseconds. @@ -375,7 +381,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_micros(&self) -> u32 { - self.nanos / NANOS_PER_MICRO + self.nanos.0 / NANOS_PER_MICRO } /// Returns the fractional part of this `Duration`, in nanoseconds. @@ -398,7 +404,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_nanos(&self) -> u32 { - self.nanos + self.nanos.0 } /// Returns the total number of whole milliseconds contained by this `Duration`. @@ -416,7 +422,7 @@ impl Duration { #[must_use] #[inline] pub const fn as_millis(&self) -> u128 { - self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos / NANOS_PER_MILLI) as u128 + self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MILLI) as u128 } /// Returns the total number of whole microseconds contained by this `Duration`. @@ -434,7 +440,7 @@ impl Duration { #[must_use] #[inline] pub const fn as_micros(&self) -> u128 { - self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos / NANOS_PER_MICRO) as u128 + self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MICRO) as u128 } /// Returns the total number of nanoseconds contained by this `Duration`. @@ -452,7 +458,7 @@ impl Duration { #[must_use] #[inline] pub const fn as_nanos(&self) -> u128 { - self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos as u128 + self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.0 as u128 } /// Checked `Duration` addition. Computes `self + other`, returning [`None`] @@ -475,7 +481,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_add(self, rhs: Duration) -> Option { if let Some(mut secs) = self.secs.checked_add(rhs.secs) { - let mut nanos = self.nanos + rhs.nanos; + let mut nanos = self.nanos.0 + rhs.nanos.0; if nanos >= NANOS_PER_SEC { nanos -= NANOS_PER_SEC; if let Some(new_secs) = secs.checked_add(1) { @@ -485,7 +491,7 @@ impl Duration { } } debug_assert!(nanos < NANOS_PER_SEC); - Some(Duration { secs, nanos }) + Some(Duration::new(secs, nanos)) } else { None } @@ -535,16 +541,16 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_sub(self, rhs: Duration) -> Option { if let Some(mut secs) = self.secs.checked_sub(rhs.secs) { - let nanos = if self.nanos >= rhs.nanos { - self.nanos - rhs.nanos + let nanos = if self.nanos.0 >= rhs.nanos.0 { + self.nanos.0 - rhs.nanos.0 } else if let Some(sub_secs) = secs.checked_sub(1) { secs = sub_secs; - self.nanos + NANOS_PER_SEC - rhs.nanos + self.nanos.0 + NANOS_PER_SEC - rhs.nanos.0 } else { return None; }; debug_assert!(nanos < NANOS_PER_SEC); - Some(Duration { secs, nanos }) + Some(Duration::new(secs, nanos)) } else { None } @@ -593,13 +599,13 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_mul(self, rhs: u32) -> Option { // Multiply nanoseconds as u64, because it cannot overflow that way. - let total_nanos = self.nanos as u64 * rhs as u64; + let total_nanos = self.nanos.0 as u64 * rhs as u64; let extra_secs = total_nanos / (NANOS_PER_SEC as u64); let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; if let Some(s) = self.secs.checked_mul(rhs as u64) { if let Some(secs) = s.checked_add(extra_secs) { debug_assert!(nanos < NANOS_PER_SEC); - return Some(Duration { secs, nanos }); + return Some(Duration::new(secs, nanos)); } } None @@ -653,9 +659,9 @@ impl Duration { let secs = self.secs / (rhs as u64); let carry = self.secs - secs * (rhs as u64); let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64); - let nanos = self.nanos / rhs + (extra_nanos as u32); + let nanos = self.nanos.0 / rhs + (extra_nanos as u32); debug_assert!(nanos < NANOS_PER_SEC); - Some(Duration { secs, nanos }) + Some(Duration::new(secs, nanos)) } else { None } @@ -677,7 +683,7 @@ impl Duration { #[inline] #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] pub const fn as_secs_f64(&self) -> f64 { - (self.secs as f64) + (self.nanos as f64) / (NANOS_PER_SEC as f64) + (self.secs as f64) + (self.nanos.0 as f64) / (NANOS_PER_SEC as f64) } /// Returns the number of seconds contained by this `Duration` as `f32`. @@ -696,7 +702,7 @@ impl Duration { #[inline] #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] pub const fn as_secs_f32(&self) -> f32 { - (self.secs as f32) + (self.nanos as f32) / (NANOS_PER_SEC as f32) + (self.secs as f32) + (self.nanos.0 as f32) / (NANOS_PER_SEC as f32) } /// Creates a new `Duration` from the specified number of seconds represented @@ -987,13 +993,13 @@ macro_rules! sum_durations { for entry in $iter { total_secs = total_secs.checked_add(entry.secs).expect("overflow in iter::sum over durations"); - total_nanos = match total_nanos.checked_add(entry.nanos as u64) { + total_nanos = match total_nanos.checked_add(entry.nanos.0 as u64) { Some(n) => n, None => { total_secs = total_secs .checked_add(total_nanos / NANOS_PER_SEC as u64) .expect("overflow in iter::sum over durations"); - (total_nanos % NANOS_PER_SEC as u64) + entry.nanos as u64 + (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.0 as u64 } }; } @@ -1001,7 +1007,7 @@ macro_rules! sum_durations { .checked_add(total_nanos / NANOS_PER_SEC as u64) .expect("overflow in iter::sum over durations"); total_nanos = total_nanos % NANOS_PER_SEC as u64; - Duration { secs: total_secs, nanos: total_nanos as u32 } + Duration::new(total_secs, total_nanos as u32) }}; } @@ -1166,27 +1172,27 @@ impl fmt::Debug for Duration { let prefix = if f.sign_plus() { "+" } else { "" }; if self.secs > 0 { - fmt_decimal(f, self.secs, self.nanos, NANOS_PER_SEC / 10, prefix, "s") - } else if self.nanos >= NANOS_PER_MILLI { + fmt_decimal(f, self.secs, self.nanos.0, NANOS_PER_SEC / 10, prefix, "s") + } else if self.nanos.0 >= NANOS_PER_MILLI { fmt_decimal( f, - (self.nanos / NANOS_PER_MILLI) as u64, - self.nanos % NANOS_PER_MILLI, + (self.nanos.0 / NANOS_PER_MILLI) as u64, + self.nanos.0 % NANOS_PER_MILLI, NANOS_PER_MILLI / 10, prefix, "ms", ) - } else if self.nanos >= NANOS_PER_MICRO { + } else if self.nanos.0 >= NANOS_PER_MICRO { fmt_decimal( f, - (self.nanos / NANOS_PER_MICRO) as u64, - self.nanos % NANOS_PER_MICRO, + (self.nanos.0 / NANOS_PER_MICRO) as u64, + self.nanos.0 % NANOS_PER_MICRO, NANOS_PER_MICRO / 10, prefix, "µs", ) } else { - fmt_decimal(f, self.nanos as u64, 0, 1, prefix, "ns") + fmt_decimal(f, self.nanos.0 as u64, 0, 1, prefix, "ns") } } } @@ -1317,7 +1323,7 @@ macro_rules! try_from_secs { return Err(FromFloatSecsError { kind: FromFloatSecsErrorKind::OverflowOrNan }); }; - Ok(Duration { secs, nanos }) + Ok(Duration::new(secs, nanos)) }}; } diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 463f714064c61..6eb7cbea6269d 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -603,7 +603,7 @@ pub fn home_dir() -> Option { /// # Platform-specific behavior /// /// On Unix, returns the value of the `TMPDIR` environment variable if it is -/// set, otherwise for non-Android it returns `/tmp`. If Android, since there +/// set, otherwise for non-Android it returns `/tmp`. On Android, since there /// is no global temporary folder (it is usually allocated per-app), it returns /// `/data/local/tmp`. /// On Windows, the behavior is equivalent to that of [`GetTempPath2`][GetTempPath2] / diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs index a456947534a45..c6aa7c77dbc41 100644 --- a/library/std/src/os/fd/mod.rs +++ b/library/std/src/os/fd/mod.rs @@ -1,16 +1,25 @@ //! Owned and borrowed Unix-like file descriptors. +//! +//! This module is supported on Unix platforms and WASI, which both use a +//! similar file descriptor system for referencing OS resources. #![stable(feature = "io_safety", since = "1.63.0")] #![deny(unsafe_op_in_unsafe_fn)] // `RawFd`, `AsRawFd`, etc. -pub mod raw; +mod raw; // `OwnedFd`, `AsFd`, etc. -pub mod owned; +mod owned; // Implementations for `AsRawFd` etc. for network types. mod net; #[cfg(test)] mod tests; + +// Export the types and traits for the public API. +#[unstable(feature = "os_fd", issue = "98699")] +pub use owned::*; +#[unstable(feature = "os_fd", issue = "98699")] +pub use raw::*; diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 71e33fb9ed864..9875c389d8aaf 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -206,10 +206,8 @@ pub trait AsFd { /// ```rust,no_run /// use std::fs::File; /// # use std::io; - /// # #[cfg(target_os = "wasi")] - /// # use std::os::wasi::io::{AsFd, BorrowedFd}; - /// # #[cfg(unix)] - /// # use std::os::unix::io::{AsFd, BorrowedFd}; + /// # #[cfg(any(unix, target_os = "wasi"))] + /// # use std::os::fd::{AsFd, BorrowedFd}; /// /// let mut f = File::open("foo.txt")?; /// # #[cfg(any(unix, target_os = "wasi"))] diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 1b3d110426feb..f92a05066706d 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -42,10 +42,8 @@ pub trait AsRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; - /// #[cfg(unix)] - /// use std::os::unix::io::{AsRawFd, RawFd}; - /// #[cfg(target_os = "wasi")] - /// use std::os::wasi::io::{AsRawFd, RawFd}; + /// #[cfg(any(unix, target_os = "wasi"))] + /// use std::os::fd::{AsRawFd, RawFd}; /// /// let mut f = File::open("foo.txt")?; /// // Note that `raw_fd` is only valid as long as `f` exists. @@ -83,10 +81,8 @@ pub trait FromRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; - /// #[cfg(unix)] - /// use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd}; - /// #[cfg(target_os = "wasi")] - /// use std::os::wasi::io::{FromRawFd, IntoRawFd, RawFd}; + /// #[cfg(any(unix, target_os = "wasi"))] + /// use std::os::fd::{FromRawFd, IntoRawFd, RawFd}; /// /// let f = File::open("foo.txt")?; /// # #[cfg(any(unix, target_os = "wasi"))] @@ -121,10 +117,8 @@ pub trait IntoRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; - /// #[cfg(unix)] - /// use std::os::unix::io::{IntoRawFd, RawFd}; - /// #[cfg(target_os = "wasi")] - /// use std::os::wasi::io::{IntoRawFd, RawFd}; + /// #[cfg(any(unix, target_os = "wasi"))] + /// use std::os::fd::{IntoRawFd, RawFd}; /// /// let f = File::open("foo.txt")?; /// #[cfg(any(unix, target_os = "wasi"))] diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 18c64b5100764..f62f5af774f0e 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -147,7 +147,7 @@ pub mod solid; pub mod vxworks; #[cfg(any(unix, target_os = "wasi", doc))] -mod fd; +pub mod fd; #[cfg(any(target_os = "linux", target_os = "android", doc))] mod net; diff --git a/library/std/src/os/unix/io/fd.rs b/library/std/src/os/unix/io/fd.rs deleted file mode 100644 index d4cb696459b7e..0000000000000 --- a/library/std/src/os/unix/io/fd.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! Owned and borrowed file descriptors. - -// Tests for this module -#[cfg(test)] -mod tests; - -#[stable(feature = "io_safety", since = "1.63.0")] -pub use crate::os::fd::owned::*; diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs index 3ab5606f8897d..25b5dbff14f30 100644 --- a/library/std/src/os/unix/io/mod.rs +++ b/library/std/src/os/unix/io/mod.rs @@ -77,10 +77,9 @@ #![stable(feature = "rust1", since = "1.0.0")] -mod fd; -mod raw; - -#[stable(feature = "io_safety", since = "1.63.0")] -pub use fd::*; #[stable(feature = "rust1", since = "1.0.0")] -pub use raw::*; +pub use crate::os::fd::*; + +// Tests for this module +#[cfg(test)] +mod tests; diff --git a/library/std/src/os/unix/io/raw.rs b/library/std/src/os/unix/io/raw.rs deleted file mode 100644 index a4d2ba797d9c4..0000000000000 --- a/library/std/src/os/unix/io/raw.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! Unix-specific extensions to general I/O primitives. - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "rust1", since = "1.0.0")] -pub use crate::os::fd::raw::*; diff --git a/library/std/src/os/unix/io/fd/tests.rs b/library/std/src/os/unix/io/tests.rs similarity index 100% rename from library/std/src/os/unix/io/fd/tests.rs rename to library/std/src/os/unix/io/tests.rs diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs index 6c884e2eaf471..d528590d75b9a 100644 --- a/library/std/src/os/wasi/io/mod.rs +++ b/library/std/src/os/wasi/io/mod.rs @@ -1,12 +1,6 @@ //! WASI-specific extensions to general I/O primitives. -#![deny(unsafe_op_in_unsafe_fn)] #![unstable(feature = "wasi_ext", issue = "71213")] -mod fd; -mod raw; - -#[unstable(feature = "wasi_ext", issue = "71213")] -pub use fd::*; #[unstable(feature = "wasi_ext", issue = "71213")] -pub use raw::*; +pub use crate::os::fd::*; diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index dff973f59d1a7..cca9c67670161 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -6,6 +6,12 @@ pub use self::inner::Instant; const NSEC_PER_SEC: u64 = 1_000_000_000; pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +#[rustc_layout_scalar_valid_range_end(999_999_999)] +struct Nanoseconds(u32); + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SystemTime { pub(in crate::sys::unix) t: Timespec, @@ -14,7 +20,7 @@ pub struct SystemTime { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(in crate::sys::unix) struct Timespec { tv_sec: i64, - tv_nsec: i64, + tv_nsec: Nanoseconds, } impl SystemTime { @@ -46,18 +52,20 @@ impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec) + .field("tv_nsec", &self.t.tv_nsec.0) .finish() } } impl Timespec { pub const fn zero() -> Timespec { - Timespec { tv_sec: 0, tv_nsec: 0 } + Timespec::new(0, 0) } - fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { - Timespec { tv_sec, tv_nsec } + const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); + // SAFETY: The assert above checks tv_nsec is within the valid range + Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } } pub fn sub_timespec(&self, other: &Timespec) -> Result { @@ -75,12 +83,12 @@ impl Timespec { // // Ideally this code could be rearranged such that it more // directly expresses the lower-cost behavior we want from it. - let (secs, nsec) = if self.tv_nsec >= other.tv_nsec { - ((self.tv_sec - other.tv_sec) as u64, (self.tv_nsec - other.tv_nsec) as u32) + let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 { + ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0) } else { ( (self.tv_sec - other.tv_sec - 1) as u64, - self.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.tv_nsec as u32, + self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0, ) }; @@ -102,7 +110,7 @@ impl Timespec { // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.tv_nsec as u32; + let mut nsec = other.subsec_nanos() + self.tv_nsec.0; if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; secs = secs.checked_add(1)?; @@ -118,7 +126,7 @@ impl Timespec { .and_then(|secs| self.tv_sec.checked_sub(secs))?; // Similar to above, nanos can't overflow. - let mut nsec = self.tv_nsec as i32 - other.subsec_nanos() as i32; + let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; secs = secs.checked_sub(1)?; @@ -130,7 +138,7 @@ impl Timespec { pub fn to_timespec(&self) -> Option { Some(libc::timespec { tv_sec: self.tv_sec.try_into().ok()?, - tv_nsec: self.tv_nsec.try_into().ok()?, + tv_nsec: self.tv_nsec.0.try_into().ok()?, }) } } @@ -293,7 +301,7 @@ mod inner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Instant") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec) + .field("tv_nsec", &self.t.tv_nsec.0) .finish() } } @@ -334,7 +342,7 @@ mod inner { let mut t = MaybeUninit::uninit(); cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); let t = unsafe { t.assume_init() }; - return Timespec { tv_sec: t.tv_sec, tv_nsec: t.tv_nsec as i64 }; + return Timespec::new(t.tv_sec, t.tv_nsec as i64); } } diff --git a/src/etc/natvis/libcore.natvis b/src/etc/natvis/libcore.natvis index a4e8a57e4b16d..624d8cc5cc55a 100644 --- a/src/etc/natvis/libcore.natvis +++ b/src/etc/natvis/libcore.natvis @@ -154,10 +154,10 @@ - {secs,d}s {nanos,d}ns + {secs,d}s {nanos.__0,d}ns secs,d - nanos,d + nanos.__0,d diff --git a/src/test/rustdoc-js-std/asrawfd.js b/src/test/rustdoc-js-std/asrawfd.js index fd228a59099e9..369a34f9c6eb7 100644 --- a/src/test/rustdoc-js-std/asrawfd.js +++ b/src/test/rustdoc-js-std/asrawfd.js @@ -6,9 +6,9 @@ const EXPECTED = { 'others': [ // Reproduction test for https://github.com/rust-lang/rust/issues/78724 // Validate that type alias methods get the correct path. - { 'path': 'std::os::unix::io::AsRawFd', 'name': 'as_raw_fd' }, - { 'path': 'std::os::wasi::io::AsRawFd', 'name': 'as_raw_fd' }, + { 'path': 'std::os::fd::AsRawFd', 'name': 'as_raw_fd' }, + { 'path': 'std::os::fd::AsRawFd', 'name': 'as_raw_fd' }, { 'path': 'std::os::linux::process::PidFd', 'name': 'as_raw_fd' }, - { 'path': 'std::os::unix::io::RawFd', 'name': 'as_raw_fd' }, + { 'path': 'std::os::fd::RawFd', 'name': 'as_raw_fd' }, ], }; diff --git a/src/test/ui/error-codes/E0585.stderr b/src/test/ui/error-codes/E0585.stderr index 7a31c4896ee8e..53c82fb416b81 100644 --- a/src/test/ui/error-codes/E0585.stderr +++ b/src/test/ui/error-codes/E0585.stderr @@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything LL | /// Hello! I'm useless... | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: doc comments must come before what they document, maybe a comment was intended with `//`? + = help: doc comments must come before what they document, if a comment was intended use `//` error: aborting due to previous error diff --git a/src/test/ui/generics/issue-94923.rs b/src/test/ui/generics/issue-94923.rs new file mode 100644 index 0000000000000..d337a5dffc934 --- /dev/null +++ b/src/test/ui/generics/issue-94923.rs @@ -0,0 +1,49 @@ +// run-pass +// regression test for issue #94923 +// min-llvm-version: 15.0.0 +// compile-flags: -C opt-level=3 + +fn f0(mut x: usize) -> usize { + for _ in 0..1000 { + x *= 123; + x %= 99 + } + x + 321 // function composition is not just longer iteration +} + +fn f1(x: usize) -> usize { + f0::<(i8, T)>(f0::<(u8, T)>(x)) +} + +fn f2(x: usize) -> usize { + f1::<(i8, T)>(f1::<(u8, T)>(x)) +} + +fn f3(x: usize) -> usize { + f2::<(i8, T)>(f2::<(u8, T)>(x)) +} + +fn f4(x: usize) -> usize { + f3::<(i8, T)>(f3::<(u8, T)>(x)) +} + +fn f5(x: usize) -> usize { + f4::<(i8, T)>(f4::<(u8, T)>(x)) +} + +fn f6(x: usize) -> usize { + f5::<(i8, T)>(f5::<(u8, T)>(x)) +} + +fn f7(x: usize) -> usize { + f6::<(i8, T)>(f6::<(u8, T)>(x)) +} + +fn f8(x: usize) -> usize { + f7::<(i8, T)>(f7::<(u8, T)>(x)) +} + +fn main() { + let y = f8::<()>(1); + assert_eq!(y, 348); +} diff --git a/src/test/ui/parser/doc-after-struct-field.rs b/src/test/ui/parser/doc-after-struct-field.rs index 5b6f080360336..03faa6733e21f 100644 --- a/src/test/ui/parser/doc-after-struct-field.rs +++ b/src/test/ui/parser/doc-after-struct-field.rs @@ -1,13 +1,13 @@ struct X { a: u8 /** document a */, //~^ ERROR found a documentation comment that doesn't document anything - //~| HELP maybe a comment was intended + //~| HELP if a comment was intended use `//` } struct Y { a: u8 /// document a //~^ ERROR found a documentation comment that doesn't document anything - //~| HELP maybe a comment was intended + //~| HELP if a comment was intended use `//` } fn main() { diff --git a/src/test/ui/parser/doc-after-struct-field.stderr b/src/test/ui/parser/doc-after-struct-field.stderr index e3b32a7f03543..ae177f1a2d1eb 100644 --- a/src/test/ui/parser/doc-after-struct-field.stderr +++ b/src/test/ui/parser/doc-after-struct-field.stderr @@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything LL | a: u8 /** document a */, | ^^^^^^^^^^^^^^^^^ | - = help: doc comments must come before what they document, maybe a comment was intended with `//`? + = help: doc comments must come before what they document, if a comment was intended use `//` error[E0585]: found a documentation comment that doesn't document anything --> $DIR/doc-after-struct-field.rs:8:11 @@ -12,7 +12,7 @@ error[E0585]: found a documentation comment that doesn't document anything LL | a: u8 /// document a | ^^^^^^^^^^^^^^ | - = help: doc comments must come before what they document, maybe a comment was intended with `//`? + = help: doc comments must come before what they document, if a comment was intended use `//` error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/doc-before-extern-rbrace.stderr b/src/test/ui/parser/doc-before-extern-rbrace.stderr index 0edceb268a7f5..8fa12ec261e09 100644 --- a/src/test/ui/parser/doc-before-extern-rbrace.stderr +++ b/src/test/ui/parser/doc-before-extern-rbrace.stderr @@ -4,7 +4,7 @@ error[E0584]: found a documentation comment that doesn't document anything LL | /// hi | ^^^^^^ this doc comment doesn't document anything | - = help: doc comments must come before what they document, maybe a comment was intended with `//`? + = help: doc comments must come before what they document, if a comment was intended use `//` error: aborting due to previous error diff --git a/src/test/ui/parser/doc-before-fn-rbrace.rs b/src/test/ui/parser/doc-before-fn-rbrace.rs index eb355136f1e66..c850216485452 100644 --- a/src/test/ui/parser/doc-before-fn-rbrace.rs +++ b/src/test/ui/parser/doc-before-fn-rbrace.rs @@ -1,5 +1,5 @@ fn main() { /// document //~^ ERROR found a documentation comment that doesn't document anything - //~| HELP maybe a comment was intended + //~| HELP if a comment was intended use `//` } diff --git a/src/test/ui/parser/doc-before-fn-rbrace.stderr b/src/test/ui/parser/doc-before-fn-rbrace.stderr index 56241de709247..6ea68e42b4cd3 100644 --- a/src/test/ui/parser/doc-before-fn-rbrace.stderr +++ b/src/test/ui/parser/doc-before-fn-rbrace.stderr @@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything LL | /// document | ^^^^^^^^^^^^ | - = help: doc comments must come before what they document, maybe a comment was intended with `//`? + = help: doc comments must come before what they document, if a comment was intended use `//` error: aborting due to previous error diff --git a/src/test/ui/parser/doc-before-rbrace.rs b/src/test/ui/parser/doc-before-rbrace.rs index 8ff946344aee8..570306f2cdf83 100644 --- a/src/test/ui/parser/doc-before-rbrace.rs +++ b/src/test/ui/parser/doc-before-rbrace.rs @@ -1,5 +1,5 @@ fn main() { println!("Hi"); /// hi //~^ ERROR found a documentation comment that doesn't document anything - //~| HELP maybe a comment was intended + //~| HELP if a comment was intended use `//` } diff --git a/src/test/ui/parser/doc-before-rbrace.stderr b/src/test/ui/parser/doc-before-rbrace.stderr index 55719cf641195..4d4741dfe59cd 100644 --- a/src/test/ui/parser/doc-before-rbrace.stderr +++ b/src/test/ui/parser/doc-before-rbrace.stderr @@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything LL | println!("Hi"); /// hi | ^^^^^^ | - = help: doc comments must come before what they document, maybe a comment was intended with `//`? + = help: doc comments must come before what they document, if a comment was intended use `//` error: aborting due to previous error diff --git a/src/test/ui/parser/doc-before-semi.rs b/src/test/ui/parser/doc-before-semi.rs index 405a7e1e2a33b..444b5874ea286 100644 --- a/src/test/ui/parser/doc-before-semi.rs +++ b/src/test/ui/parser/doc-before-semi.rs @@ -1,6 +1,6 @@ fn main() { /// hi //~^ ERROR found a documentation comment that doesn't document anything - //~| HELP maybe a comment was intended + //~| HELP if a comment was intended use `//` ; } diff --git a/src/test/ui/parser/doc-before-semi.stderr b/src/test/ui/parser/doc-before-semi.stderr index e6bade18d0a2d..a879e13ffbd62 100644 --- a/src/test/ui/parser/doc-before-semi.stderr +++ b/src/test/ui/parser/doc-before-semi.stderr @@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything LL | /// hi | ^^^^^^ | - = help: doc comments must come before what they document, maybe a comment was intended with `//`? + = help: doc comments must come before what they document, if a comment was intended use `//` error: aborting due to previous error diff --git a/src/test/ui/parser/doc-before-struct-rbrace-1.rs b/src/test/ui/parser/doc-before-struct-rbrace-1.rs index 3866a3105c25a..0c8d90c3b0371 100644 --- a/src/test/ui/parser/doc-before-struct-rbrace-1.rs +++ b/src/test/ui/parser/doc-before-struct-rbrace-1.rs @@ -2,7 +2,7 @@ struct X { a: u8, /// document //~^ ERROR found a documentation comment that doesn't document anything - //~| HELP maybe a comment was intended + //~| HELP if a comment was intended use `//` } fn main() { diff --git a/src/test/ui/parser/doc-before-struct-rbrace-1.stderr b/src/test/ui/parser/doc-before-struct-rbrace-1.stderr index 92b5133b74dd6..94934f734b394 100644 --- a/src/test/ui/parser/doc-before-struct-rbrace-1.stderr +++ b/src/test/ui/parser/doc-before-struct-rbrace-1.stderr @@ -7,7 +7,7 @@ LL | a: u8, LL | /// document | ^^^^^^^^^^^^ | - = help: doc comments must come before what they document, maybe a comment was intended with `//`? + = help: doc comments must come before what they document, if a comment was intended use `//` error: aborting due to previous error diff --git a/src/test/ui/parser/doc-before-struct-rbrace-2.rs b/src/test/ui/parser/doc-before-struct-rbrace-2.rs index dda138f1a8835..2b2aadf7984a2 100644 --- a/src/test/ui/parser/doc-before-struct-rbrace-2.rs +++ b/src/test/ui/parser/doc-before-struct-rbrace-2.rs @@ -1,7 +1,7 @@ struct X { a: u8 /// document //~^ ERROR found a documentation comment that doesn't document anything - //~| HELP maybe a comment was intended + //~| HELP if a comment was intended use `//` } fn main() { diff --git a/src/test/ui/parser/doc-before-struct-rbrace-2.stderr b/src/test/ui/parser/doc-before-struct-rbrace-2.stderr index b25ccab79f94a..6b5c8c1f8b531 100644 --- a/src/test/ui/parser/doc-before-struct-rbrace-2.stderr +++ b/src/test/ui/parser/doc-before-struct-rbrace-2.stderr @@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything LL | a: u8 /// document | ^^^^^^^^^^^^ | - = help: doc comments must come before what they document, maybe a comment was intended with `//`? + = help: doc comments must come before what they document, if a comment was intended use `//` error: aborting due to previous error diff --git a/src/test/ui/parser/doc-inside-trait-item.stderr b/src/test/ui/parser/doc-inside-trait-item.stderr index 246255a0a4675..900124adcc3b8 100644 --- a/src/test/ui/parser/doc-inside-trait-item.stderr +++ b/src/test/ui/parser/doc-inside-trait-item.stderr @@ -4,7 +4,7 @@ error[E0584]: found a documentation comment that doesn't document anything LL | /// empty doc | ^^^^^^^^^^^^^ this doc comment doesn't document anything | - = help: doc comments must come before what they document, maybe a comment was intended with `//`? + = help: doc comments must come before what they document, if a comment was intended use `//` error: aborting due to previous error diff --git a/src/test/ui/parser/issues/issue-34222-1.stderr b/src/test/ui/parser/issues/issue-34222-1.stderr index 0799656b06bd2..b451484ba224b 100644 --- a/src/test/ui/parser/issues/issue-34222-1.stderr +++ b/src/test/ui/parser/issues/issue-34222-1.stderr @@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything LL | /// comment | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: doc comments must come before what they document, maybe a comment was intended with `//`? + = help: doc comments must come before what they document, if a comment was intended use `//` error: aborting due to previous error diff --git a/src/test/ui/parser/issues/issue-48636.stderr b/src/test/ui/parser/issues/issue-48636.stderr index 1a6e4cfd2b203..6177870d1cee9 100644 --- a/src/test/ui/parser/issues/issue-48636.stderr +++ b/src/test/ui/parser/issues/issue-48636.stderr @@ -8,7 +8,7 @@ LL | x: u8 LL | /// The ID of the parent core | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: doc comments must come before what they document, maybe a comment was intended with `//`? + = help: doc comments must come before what they document, if a comment was intended use `//` error: aborting due to previous error diff --git a/src/test/ui/regions/issue-102374.rs b/src/test/ui/regions/issue-102374.rs new file mode 100644 index 0000000000000..e0a1164211a2c --- /dev/null +++ b/src/test/ui/regions/issue-102374.rs @@ -0,0 +1,20 @@ +use std::cell::Cell; + +#[rustfmt::skip] +fn f( + f: for<'a, 'b, 'c, 'd, 'e, 'f, 'g, + 'h, 'i, 'j, 'k, 'l, 'm, 'n, + 'o, 'p, 'q, 'r, 's, 't, 'u, + 'v, 'w, 'x, 'y, 'z, 'z0> + fn(Cell<(& i32, &'a i32, &'b i32, &'c i32, &'d i32, + &'e i32, &'f i32, &'g i32, &'h i32, &'i i32, + &'j i32, &'k i32, &'l i32, &'m i32, &'n i32, + &'o i32, &'p i32, &'q i32, &'r i32, &'s i32, + &'t i32, &'u i32, &'v i32, &'w i32, &'x i32, + &'y i32, &'z i32, &'z0 i32)>), +) -> i32 { + f + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/src/test/ui/regions/issue-102374.stderr b/src/test/ui/regions/issue-102374.stderr new file mode 100644 index 0000000000000..31b855c36bead --- /dev/null +++ b/src/test/ui/regions/issue-102374.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/issue-102374.rs:16:5 + | +LL | ) -> i32 { + | --- expected `i32` because of return type +LL | f + | ^ expected `i32`, found fn pointer + | + = note: expected type `i32` + found fn pointer `for<'z1, 'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x, 'y, 'z, 'z0> fn(Cell<(&'z1 i32, &'a i32, &'b i32, &'c i32, &'d i32, &'e i32, &'f i32, &'g i32, &'h i32, &'i i32, &'j i32, &'k i32, &'l i32, &'m i32, &'n i32, &'o i32, &'p i32, &'q i32, &'r i32, &'s i32, &'t i32, &'u i32, &'v i32, &'w i32, &'x i32, &'y i32, &'z i32, &'z0 i32)>)` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`.