diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index a8898acf3a415..c995cdee10fe9 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -617,6 +617,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr_and_args.map_or(expr.span, |(e, _)| e.span), expr.span, expr.hir_id, + call_expr_and_args.is_some(), ) .0 } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index e655e0857d858..ea2e3584b2db7 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1006,6 +1006,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, path_span: Span, hir_id: HirId, + has_args: bool, ) -> (Ty<'tcx>, Res) { let tcx = self.tcx; @@ -1253,17 +1254,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "the `Self` constructor can only be used with tuple or unit structs", ); if let Some(adt_def) = ty.normalized.ty_adt_def() { - match adt_def.adt_kind() { - AdtKind::Enum => { - err.help("did you mean to use one of the enum's variants?"); - } - AdtKind::Struct | AdtKind::Union => { - err.span_suggestion( - span, - "use curly brackets", - "Self { /* fields */ }", - Applicability::HasPlaceholders, - ); + let def_id = self.body_def_id.to_def_id(); + if !has_args + && let Some(assoc) = tcx.opt_associated_item(def_id) + && assoc.is_method() + { + let self_ty = + tcx.fn_sig(def_id).instantiate_identity().skip_binder().inputs()[0]; + let applicability = if let ty::Adt(..) = self_ty.kind() { + // We're within a method that takes ownership of `Self`, likely a + // builder, so this is most likely a typo. + Applicability::MachineApplicable + } else { + // We still might have meant `self` instead of `Self`. + Applicability::MaybeIncorrect + }; + err.span_suggestion_verbose( + span, + format!( + "you might have meant to refer to the `self` binding of type \ + `{self_ty}`", + ), + "self".to_string(), + applicability, + ); + } else { + match adt_def.adt_kind() { + AdtKind::Enum => { + err.span_help( + tcx.def_span(adt_def.did()), + if adt_def.variants().is_empty() { + "the enum is unconstructable because it has no variants" + } else { + "you might have meant to use one of the enum's variants" + }, + ); + } + AdtKind::Struct | AdtKind::Union => { + err.span_suggestion_verbose( + span, + "use curly brackets", + "Self { /* fields */ }", + Applicability::HasPlaceholders, + ); + } } } } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index caffef6a217a8..ec4483b62fe72 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -911,7 +911,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rustc_hir::PatExprKind::Path(qpath) => { let (res, opt_ty, segments) = self.resolve_ty_and_res_fully_qualified_call(qpath, lt.hir_id, lt.span); - self.instantiate_value_path(segments, opt_ty, res, lt.span, lt.span, lt.hir_id).0 + self.instantiate_value_path( + segments, opt_ty, res, lt.span, lt.span, lt.hir_id, false, + ) + .0 } }; self.write_ty(lt.hir_id, ty); @@ -1624,7 +1627,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Find the type of the path pattern, for later checking. let (pat_ty, pat_res) = - self.instantiate_value_path(segments, opt_ty, res, span, span, path_id); + self.instantiate_value_path(segments, opt_ty, res, span, span, path_id, false); Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Path { res, pat_res, segments } }) } @@ -1784,8 +1787,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Type-check the path. - let (pat_ty, res) = - self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id); + let (pat_ty, res) = self + .instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id, false); if !pat_ty.is_fn() { return report_unexpected_res(res); } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 50e823f88178f..6e838c4454c4c 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -411,6 +411,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.impl_super_outlives(impl_def_id) } + fn supertrait_def_ids(self, trait_def_id: DefId) -> impl Iterator { + rustc_type_ir::elaborate::supertrait_def_ids(self, trait_def_id) + } + fn impl_is_const(self, def_id: DefId) -> bool { debug_assert_matches!(self.def_kind(def_id), DefKind::Impl { of_trait: true }); self.is_conditionally_const(def_id) diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs index 5b25bdc01117b..e8ff3c3a08b79 100644 --- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs +++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs @@ -2,7 +2,7 @@ use rustc_hir::HirId; use rustc_lint_defs::builtin::CONST_ITEM_MUTATION; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::Span; use rustc_span::def_id::DefId; @@ -35,8 +35,9 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> { fn is_const_item_without_destructor(&self, local: Local) -> Option { let def_id = self.is_const_item(local)?; - // We avoid linting mutation of a const item if the const's type has a - // Drop impl. The Drop logic observes the mutation which was performed. + // We avoid linting mutation of a const item if the const's type needs + // drop. Any drop logic (including that of fields) may observe the + // mutation which was performed. // // pub struct Log { msg: &'static str } // pub const LOG: Log = Log { msg: "" }; @@ -46,21 +47,30 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> { // // LOG.msg = "wow"; // prints "wow" // + // Likewise, if a field of the const type has its own Drop impl, that + // drop logic may also observe the mutation: + // + // struct Inner { val: u32 } + // impl Drop for Inner { fn drop(&mut self) { println!("{}", self.val); } } + // struct Outer { inner: Inner } + // const O: Outer = Outer { inner: Inner { val: 0 } }; + // + // O.inner.val = 42; // Inner::drop prints "42" + // // FIXME(https://github.com/rust-lang/rust/issues/77425): // Drop this exception once there is a stable attribute to suppress the - // const item mutation lint for a single specific const only. Something - // equivalent to: - // - // #[const_mutation_allowed] - // pub const LOG: Log = Log { msg: "" }; - // FIXME: this should not be checking for `Drop` impls, - // but whether it or any field has a Drop impl (`needs_drop`) - // as fields' Drop impls may make this observable, too. - match self.tcx.type_of(def_id).skip_binder().ty_adt_def().map(|adt| adt.has_dtor(self.tcx)) - { - Some(true) => None, - Some(false) | None => Some(def_id), + // const item mutation lint for a single specific const only. + let ty = self.tcx.type_of(def_id).instantiate_identity().skip_norm_wip(); + // `needs_drop` is overly conservative for types that contain type + // parameters (e.g. `Self` in a trait associated const): it always + // returns `true` because the parameter *might* implement Drop, even + // when the concrete type at the call site does not. In that case we + // cannot suppress the lint, so fall through and warn. + if ty.has_param() { + return Some(def_id); } + let typing_env = ty::TypingEnv::non_body_analysis(self.tcx, def_id); + if ty.needs_drop(self.tcx, typing_env) { None } else { Some(def_id) } } /// If we should lint on this usage, return the [`HirId`], source [`Span`] diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 294c887b5062c..79db6ba43e228 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1096,7 +1096,8 @@ where .auto_traits() .into_iter() .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| { - elaborate::supertrait_def_ids(self.cx(), principal_def_id) + self.cx() + .supertrait_def_ids(principal_def_id) .filter(|def_id| self.cx().trait_is_auto(*def_id)) })) .collect(); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 58e98a64b5e41..6bc42a0a40734 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -210,9 +210,7 @@ impl<'a> Parser<'a> { let (rhs, span) = finish_parsing_bin_op(self)?; self.mk_expr(span, ExprKind::Assign(lhs, rhs, op.span)) } - AssocOp::Cast => { - self.parse_assoc_op_cast(lhs, lhs_span, op.span, ExprKind::Cast)? - } + AssocOp::Cast => self.parse_assoc_op_cast(lhs, lhs_span, op.span)?, AssocOp::Range(limits) => self.parse_expr_range(min_prec, lhs, limits, op.span)?, }; @@ -555,17 +553,17 @@ impl<'a> Parser<'a> { lhs: Box, lhs_span: Span, op_span: Span, - expr_kind: fn(Box, Box) -> ExprKind, ) -> PResult<'a, Box> { - let mk_expr = |this: &mut Self, lhs: Box, rhs: Box| { - this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span), expr_kind(lhs, rhs)) + let mk_expr = |this: &mut Self, rhs: Box| { + let span = this.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span); + this.mk_expr(span, ExprKind::Cast(lhs, rhs)) }; // Save the state of the parser before parsing type normally, in case there is a // LessThan comparison after this cast. let parser_snapshot_before_type = self.clone(); let cast_expr = match self.parse_as_cast_ty() { - Ok(rhs) => mk_expr(self, lhs, rhs), + Ok(rhs) => mk_expr(self, rhs), Err(type_err) => { if !self.may_recover() { return Err(type_err); @@ -576,46 +574,11 @@ impl<'a> Parser<'a> { // `usize < y` as a type with generic arguments. let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type); - // Check for typo of `'a: loop { break 'a }` with a missing `'`. - match (&lhs.kind, &self.token.kind) { - ( - // `foo: ` - ExprKind::Path(None, ast::Path { segments, .. }), - token::Ident(kw::For | kw::Loop | kw::While, IdentIsRaw::No), - ) if let [segment] = segments.as_slice() => { - let snapshot = self.create_snapshot_for_diagnostic(); - let label = Label { - ident: Ident::from_str_and_span( - &format!("'{}", segment.ident), - segment.ident.span, - ), - }; - match self.parse_expr_labeled(label, false) { - Ok(expr) => { - type_err.cancel(); - self.dcx().emit_err(crate::diagnostics::MalformedLoopLabel { - span: label.ident.span, - suggestion: label.ident.span.shrink_to_lo(), - }); - return Ok(expr); - } - Err(err) => { - err.cancel(); - self.restore_snapshot(snapshot); - } - } - } - _ => {} - } - match self.parse_path(PathStyle::Expr) { Ok(path) => { let span_after_type = parser_snapshot_after_type.token.span; - let expr = mk_expr( - self, - lhs, - self.mk_ty(path.span, TyKind::Path(None, path.clone())), - ); + let expr = + mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path.clone()))); let args_span = self.look_ahead(1, |t| t.span).to(span_after_type); match self.token.kind { @@ -674,48 +637,38 @@ impl<'a> Parser<'a> { // written `((&x) as T)[0]`. let span = cast_expr.span; - let with_postfix = self.parse_expr_dot_or_call_with(AttrVec::new(), cast_expr, span)?; // Check if an illegal postfix operator has been added after the cast. // If the resulting expression is not a cast, it is an illegal postfix operator. if !matches!(with_postfix.kind, ExprKind::Cast(_, _)) { - let msg = format!( - "cast cannot be followed by {}", - match with_postfix.kind { - ExprKind::Index(..) => "indexing", - ExprKind::Try(_) => "`?`", - ExprKind::Field(_, _) => "a field access", - ExprKind::MethodCall(_) => "a method call", - ExprKind::Call(_, _) => "a function call", - ExprKind::Await(_, _) => "`.await`", - ExprKind::Use(_, _) => "`.use`", - ExprKind::Yield(YieldKind::Postfix(_)) => "`.yield`", - ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", - ExprKind::Err(_) => return Ok(with_postfix), - _ => unreachable!( - "did not expect {:?} as an illegal postfix operator following cast", - with_postfix.kind - ), - } - ); - let mut err = self.dcx().struct_span_err(span, msg); - - let suggest_parens = |err: &mut Diag<'_>| { - let suggestions = vec![ - (span.shrink_to_lo(), "(".to_string()), - (span.shrink_to_hi(), ")".to_string()), - ]; - err.multipart_suggestion( + let kind = match with_postfix.kind { + ExprKind::Index(..) => "indexing", + ExprKind::Try(_) => "`?`", + ExprKind::Field(_, _) => "a field access", + ExprKind::MethodCall(_) => "a method call", + ExprKind::Call(_, _) => "a function call", + ExprKind::Await(_, _) => "`.await`", + ExprKind::Use(_, _) => "`.use`", + ExprKind::Yield(YieldKind::Postfix(_)) => "`.yield`", + ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", + ExprKind::Err(_) => return Ok(with_postfix), + _ => unreachable!( + "did not expect {:?} as an illegal postfix operator following cast", + with_postfix.kind + ), + }; + self.dcx() + .struct_span_err(span, format!("cast cannot be followed by {kind}")) + .with_multipart_suggestion( "try surrounding the expression in parentheses", - suggestions, + vec![ + (span.shrink_to_lo(), "(".to_string()), + (span.shrink_to_hi(), ")".to_string()), + ], Applicability::MachineApplicable, - ); - }; - - suggest_parens(&mut err); - - err.emit(); + ) + .emit(); }; Ok(with_postfix) } @@ -1401,6 +1354,9 @@ impl<'a> Parser<'a> { if let Some(expr) = this.maybe_recover_bad_struct_literal_path(false)? { return Ok(expr); } + if let Some(arr) = this.recover_from_c_array(lo) { + return Ok(arr); + } this.parse_expr_block(None, lo, BlockCheckMode::Default) } else if this.check(exp!(Or)) || this.check(exp!(OrOr)) { this.parse_expr_closure().map_err(|mut err| { @@ -2227,39 +2183,6 @@ impl<'a> Parser<'a> { } } - fn is_array_like_block(&mut self) -> bool { - self.token.kind == TokenKind::OpenBrace - && self - .look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_))) - && self.look_ahead(2, |t| t == &token::Comma) - && self.look_ahead(3, |t| t.can_begin_expr()) - } - - /// Emits a suggestion if it looks like the user meant an array but - /// accidentally used braces, causing the code to be interpreted as a block - /// expression. - fn maybe_suggest_brackets_instead_of_braces(&mut self, lo: Span) -> Option> { - let mut snapshot = self.create_snapshot_for_diagnostic(); - match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) { - Ok(arr) => { - let guar = self.dcx().emit_err(crate::diagnostics::ArrayBracketsInsteadOfBraces { - span: arr.span, - sub: crate::diagnostics::ArrayBracketsInsteadOfBracesSugg { - left: lo, - right: snapshot.prev_token.span, - }, - }); - - self.restore_snapshot(snapshot); - Some(self.mk_expr_err(arr.span, guar)) - } - Err(e) => { - e.cancel(); - None - } - } - } - fn suggest_missing_semicolon_before_array( &self, prev_span: Span, @@ -2309,12 +2232,6 @@ impl<'a> Parser<'a> { lo: Span, blk_mode: BlockCheckMode, ) -> PResult<'a, Box> { - if self.may_recover() && self.is_array_like_block() { - if let Some(arr) = self.maybe_suggest_brackets_instead_of_braces(lo) { - return Ok(arr); - } - } - if self.token.is_metavar_block() { self.dcx().emit_err(crate::diagnostics::InvalidBlockMacroSegment { span: self.token.span, diff --git a/compiler/rustc_parse/src/parser/expr/diagnostics.rs b/compiler/rustc_parse/src/parser/expr/diagnostics.rs index 707ae5d34bc75..6e56ea6c616fd 100644 --- a/compiler/rustc_parse/src/parser/expr/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/expr/diagnostics.rs @@ -3,8 +3,8 @@ use rustc_ast::{BinOpKind, Expr, ExprKind, token}; use rustc_errors::{Applicability, Diag, PResult}; use rustc_span::{Span, Spanned, respan, sym}; -use crate::diagnostics; use crate::parser::Parser; +use crate::{diagnostics, exp}; impl<'a> Parser<'a> { /// Recover from alphabetic logic operators `and` and `or` as found in e.g., Python and PHP. @@ -216,6 +216,38 @@ impl<'a> Parser<'a> { } err } + + /// Recover from array expressions as found in C like `{0, 1, 2, 3}`. + pub(super) fn recover_from_c_array(&mut self, lo: Span) -> Option> { + if !self.may_recover() + || self.token.kind != token::OpenBrace + || self.look_ahead(1, |t| !matches!(t.kind, token::Literal(_))) + || self.look_ahead(2, |t| t != &token::Comma) + || self.look_ahead(3, |t| !t.can_begin_expr()) + { + return None; + } + + let mut snapshot = self.create_snapshot_for_diagnostic(); + match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) { + Ok(arr) => { + let guar = self.dcx().emit_err(diagnostics::ArrayBracketsInsteadOfBraces { + span: arr.span, + sub: diagnostics::ArrayBracketsInsteadOfBracesSugg { + left: lo, + right: snapshot.prev_token.span, + }, + }); + + self.restore_snapshot(snapshot); + Some(self.mk_expr_err(arr.span, guar)) + } + Err(e) => { + e.cancel(); + None + } + } + } } #[derive(Copy, Clone)] diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index ff057e61f7a04..4fd3649a6dc3c 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -5,6 +5,7 @@ use std::ops::Bound; use ast::Label; use rustc_ast as ast; use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind}; +use rustc_ast::tokenstream::TokenTree; use rustc_ast::util::classify::{self, TrailingBrace}; use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{ @@ -12,7 +13,7 @@ use rustc_ast::{ LocalKind, MacCall, MacCallStmt, MacStmtStyle, Recovered, Stmt, StmtKind, }; use rustc_errors::{Applicability, Diag, PResult}; -use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, kw, sym}; +use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use super::attr::InnerAttrForbiddenReason; @@ -363,6 +364,14 @@ impl<'a> Parser<'a> { } else { (None, None, None) }; + + let init_wrapped = self + .tree_look_ahead(2, |tree| match tree { + TokenTree::Token(tok, _) => tok.is_keyword(kw::Else), + TokenTree::Delimited(..) => false, + }) + .unwrap_or(false); + let init = match (self.parse_initializer(err.is_some()), err) { (Ok(init), None) => { // init parsed, ty parsed @@ -400,6 +409,7 @@ impl<'a> Parser<'a> { return Err(err); } }; + let trailing_token = self.prev_token; let kind = match init { None => LocalKind::Decl, Some(init) => { @@ -411,8 +421,14 @@ impl<'a> Parser<'a> { return Err(self.error_block_no_opening_brace_msg(Cow::from(msg))); } let els = self.parse_block()?; - self.check_let_else_init_bool_expr(&init); - self.check_let_else_init_trailing_brace(&init); + // These checks should also respect invisible delimiter + if !init_wrapped { + self.check_let_else_init_bool_expr(&init); + } + if matches!(trailing_token.kind, TokenKind::CloseBrace) { + self.check_let_else_init_trailing_brace(&init); + } + LocalKind::InitElse(init, els) } else { LocalKind::Init(init) @@ -467,7 +483,7 @@ impl<'a> Parser<'a> { ), }; self.dcx().emit_err(diagnostics::InvalidCurlyInLetElse { - span: span.with_lo(span.hi() - BytePos(1)), + span: self.psess.source_map().end_point(span), sugg, }); } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 88c07c6e2778c..42fb9121076f4 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -3,8 +3,8 @@ use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnPtrTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, - Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, - TyKind, UnsafeBinderTy, + Path, Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, + Ty, TyKind, UnsafeBinderTy, }; use rustc_errors::{Applicability, Diag, E0516, PResult}; use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; @@ -406,7 +406,23 @@ impl<'a> Parser<'a> { let msg = format!("expected type, found {}", super::token_descr(&self.token)); let mut err = self.dcx().struct_span_err(lo, msg); err.span_label(lo, "expected type"); - return Err(err); + if self.may_recover() + && (self.eat_keyword_noexpect(kw::True) || self.eat_keyword_noexpect(kw::False)) + { + err.span_suggestion( + self.prev_token.span, + "the type is called", + "bool", + Applicability::MachineApplicable, + ); + err.emit(); + TyKind::Path( + None, + Path::from_ident(Ident { span: self.prev_token.span, name: sym::bool }), + ) + } else { + return Err(err); + } }; let span = lo.to(self.prev_token.span); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index b5235fa443575..4ccc7ab986f1e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -521,7 +521,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let found_sig = self.normalize_fn_sig(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2)); - if self.same_type_modulo_infer(expected_sig, found_sig) { + let expected_sig_anon = self.tcx.anonymize_bound_vars(expected_sig); + let found_sig_anon = self.tcx.anonymize_bound_vars(found_sig); + if self.same_type_modulo_infer(expected_sig_anon, found_sig_anon) { diag.subdiagnostic(FnUniqTypes); } diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 912a5ac90f632..2110521eae764 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -318,6 +318,9 @@ impl> Iterator for Elaborator { /// does not compute the full elaborated super-predicates but just the set of def-ids. It is used /// to identify which traits may define a given associated type to help avoid cycle errors, /// and to make size estimates for vtable layout computation. +/// +/// rust-analyzer has a query for this, so don't use this function there. +#[cfg(feature = "nightly")] pub fn supertrait_def_ids( cx: I, trait_def_id: I::TraitId, diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 289f5dc2e1b46..0a11a82c99e76 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -402,6 +402,9 @@ pub trait Interner: impl_def_id: Self::ImplId, ) -> ty::EarlyBinder>; + fn supertrait_def_ids(self, trait_def_id: Self::TraitId) + -> impl Iterator; + fn impl_is_const(self, def_id: Self::ImplId) -> bool; fn fn_is_const(self, def_id: Self::FunctionId) -> bool; fn closure_is_const(self, def_id: Self::ClosureId) -> bool; diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 5282b39782181..9d871d8c5745d 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -712,16 +712,18 @@ pub const fn needs_drop() -> bool { /// This means that, for example, the padding byte in `(u8, u16)` is not /// necessarily zeroed. /// -/// There is no guarantee that an all-zero byte-pattern represents a valid value -/// of some type `T`. For example, the all-zero byte-pattern is not a valid value -/// for reference types (`&T`, `&mut T`) and function pointers. Using `zeroed` -/// on such types causes immediate [undefined behavior][ub] because [the Rust -/// compiler assumes][inv] that there always is a valid value in a variable it -/// considers initialized. -/// /// This has the same effect as [`MaybeUninit::zeroed().assume_init()`][zeroed]. /// It is useful for FFI sometimes, but should generally be avoided. /// +/// +/// # Safety +/// +/// The all-zero byte-pattern must represent a valid value of type `T`. +/// For example, it is not valid for reference types (`&T`, `&mut T`) or function +/// pointers. Using `zeroed` on such types causes immediate [undefined behavior][ub] +/// because [the Rust compiler assumes][inv] that there always is a valid value in a +/// variable it considers initialized. +/// /// [zeroed]: MaybeUninit::zeroed /// [ub]: ../../reference/behavior-considered-undefined.html /// [inv]: MaybeUninit#initialization-invariant diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 0563c225f7e0b..d0ae6c31267f3 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1412,7 +1412,8 @@ macro_rules! nonzero_integer { } #[stable(feature = "nonzero_parse", since = "1.35.0")] - impl FromStr for NonZero<$Int> { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + const impl FromStr for NonZero<$Int> { type Err = ParseIntError; /// Parses a non-zero integer from a string slice with decimal digits. diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 8df809264a6dd..65b8ed634bc05 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1869,6 +1869,28 @@ impl Dir { pub fn remove_dir>(&self, path: P) -> io::Result<()> { self.inner.remove_dir(path.as_ref()) } + + /// Creates a new `Dir` instance that shares the same underlying directory handle + /// as the existing `Dir` instance. + /// + /// # Examples + /// + /// Creates two handles for a directory named `foo`: + /// + /// ```no_run + /// #![feature(dirfd)] + /// use std::fs::Dir; + /// + /// fn main() -> std::io::Result<()> { + /// let dir = Dir::open("foo")?; + /// let dir_copy = dir.try_clone()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "dirfd", issue = "120426")] + pub fn try_clone(&self) -> io::Result { + Ok(Dir { inner: self.inner.duplicate()? }) + } } impl AsInner for Dir { diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 148f1c32b08b9..e79b5cf17bc2b 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -775,6 +775,273 @@ fn file_test_io_seek_read_write() { check!(fs::remove_file(&filename)); } +#[test] +#[cfg(windows)] +fn file_test_io_seek_read_exact_write_all() { + use crate::os::windows::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_seek_read_exact_write_all.txt"); + let mut buf = [0; 256]; + let write1 = "asdf"; + let write2 = "qwer-"; + let write3 = "-zxcv"; + let content = "qwer-asdf-zxcv"; + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut rw = check!(oo.open(&filename)); + check!(rw.seek_write_all(write1.as_bytes(), 5)); + assert_eq!(check!(rw.stream_position()), 9); + check!(rw.seek_read_exact(&mut buf[..write1.len()], 5)); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.stream_position()), 9); + assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0); + assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); + assert_eq!(check!(rw.stream_position()), 5); + assert_eq!(check!(rw.read(&mut buf)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.stream_position()), 9); + check!(rw.seek_read_exact(&mut buf[..write2.len()], 0)); + assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); + assert_eq!(check!(rw.stream_position()), 5); + check!(rw.seek_write_all(write3.as_bytes(), 9)); + assert_eq!(check!(rw.stream_position()), 14); + } + { + let mut read = check!(File::open(&filename)); + check!(read.seek_read_exact(&mut buf[..content.len()], 0)); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.stream_position()), 14); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + check!(read.seek_read_exact(&mut buf[..content.len()], 0)); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.stream_position()), 14); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + assert_eq!(check!(read.read(&mut buf)), write3.len()); + assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); + assert_eq!(check!(read.stream_position()), 14); + check!(read.seek_read_exact(&mut buf[..content.len()], 0)); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.stream_position()), 14); + assert!(read.seek_read_exact(&mut buf, 14).is_err()); + assert!(read.seek_read_exact(&mut buf, 15).is_err()); + } + check!(fs::remove_file(&filename)); +} + +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_1() { + use crate::os::windows::fs::FileExt; + + // Test when seek_read_exact(), seek_write_all() are called with empty buffers. + struct MockFile {} + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { + panic!("should not be called"); + } + + fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { + panic!("should not be called"); + } + } + + let mock_file = MockFile {}; + check!(mock_file.seek_read_exact(&mut [], 0)); + check!(mock_file.seek_write_all(&[], 0)); + check!(mock_file.seek_read_exact(&mut [], 420)); + check!(mock_file.seek_write_all(&[], 420)); +} + +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_2() { + use crate::os::windows::fs::FileExt; + + // Test when seek_read(), seek_write() return Ok(0) + struct MockFile { + expected_offset: u64, + } + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + Ok(0) + } + + fn seek_write(&self, _buf: &[u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + Ok(0) + } + } + + { + let mock_file = MockFile { expected_offset: 0 }; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read_exact(&mut buf, 0).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + assert_eq!(mock_file.seek_write_all(&buf, 0).unwrap_err().kind(), io::ErrorKind::WriteZero); + } + + { + let mock_file = MockFile { expected_offset: 420 }; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read_exact(&mut buf, 420).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + assert_eq!( + mock_file.seek_write_all(&buf, 420).unwrap_err().kind(), + io::ErrorKind::WriteZero + ); + } +} + +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_3() { + use crate::os::windows::fs::FileExt; + + // Test that Err other than io::ErrorKind::Interrupted are propagated up. + struct MockFile { + expected_offset: u64, + } + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + Err(io::Error::new(io::ErrorKind::PermissionDenied, "seek_read")) + } + + fn seek_write(&self, _buf: &[u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + Err(io::Error::new(io::ErrorKind::ConnectionRefused, "seek_write")) + } + } + + { + let mock_file = MockFile { expected_offset: 0 }; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read_exact(&mut buf, 0).unwrap_err().kind(), + io::ErrorKind::PermissionDenied + ); + assert_eq!( + mock_file.seek_write_all(&buf, 0).unwrap_err().kind(), + io::ErrorKind::ConnectionRefused + ); + } + + { + let mock_file = MockFile { expected_offset: 420 }; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read_exact(&mut buf, 420).unwrap_err().kind(), + io::ErrorKind::PermissionDenied + ); + assert_eq!( + mock_file.seek_write_all(&buf, 420).unwrap_err().kind(), + io::ErrorKind::ConnectionRefused + ); + } + // FIXME: Cover io::ErrorKind::Interrupted, but don't infinite loop ;) +} + +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_4() { + use crate::os::windows::fs::FileExt; + + const MSG: &[u8] = + b"The Rust programming language helps you write faster, more reliable software."; + + // Test when the entire read or write is satisfied by only one call to seek_read() or + // seek_write(), respectively. + struct MockFile { + expected_offset: u64, + } + + impl FileExt for MockFile { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + assert_eq!(buf.len(), MSG.len()); + assert_eq!(buf, &[0; MSG.len()]); + buf.copy_from_slice(MSG); + Ok(MSG.len()) + } + + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + assert_eq!(buf.len(), MSG.len()); + assert_eq!(buf, MSG); + Ok(MSG.len()) + } + } + + { + let mock_file = MockFile { expected_offset: 0 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 0)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 0)); + } + + { + let mock_file = MockFile { expected_offset: 420 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 420)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 420)); + } +} + +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_5() { + use crate::os::windows::fs::FileExt; + + const MSG: &[u8] = + b"Rust is for students and those who are interested in learning about systems concepts."; + + // Test pathological case where seek_read(), seek_write() only do 1 byte per call, return Ok(1) + struct MockFile { + base_offset: u64, + } + + impl FileExt for MockFile { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + let offset = (offset - self.base_offset) as usize; + buf[0..1].copy_from_slice(&MSG[offset..offset + 1]); + Ok(1) + } + + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + let offset = (offset - self.base_offset) as usize; + assert_eq!(buf[0..1], MSG[offset..offset + 1]); + Ok(1) + } + } + + { + let mock_file = MockFile { base_offset: 0 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 0)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 0)); + } + + { + let mock_file = MockFile { base_offset: 420 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 420)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 420)); + } +} + #[test] #[cfg(windows)] fn test_seek_read_buf() { @@ -2726,6 +2993,19 @@ fn test_dir_read_file() { assert_eq!("bar", &buf); } +#[test] +fn test_dir_clone() { + let tmpdir = tmpdir(); + let mut f = check!(File::create(tmpdir.join("foo.txt"))); + check!(f.write_all(b"bar")); + drop(f); + + let dir = check!(Dir::open(tmpdir.path())); + let dir2 = check!(dir.try_clone()); + let f = check!(dir2.open_file("foo.txt")); + drop(f); +} + #[test] fn test_dir_metadata() { let tmpdir = tmpdir(); diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 3daddc2d34323..dd6e4a690e326 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -123,6 +123,8 @@ impl SocketAddr { .map_or(len, |new_len| (new_len + SUN_PATH_OFFSET) as libc::socklen_t); } + len = len.min(size_of::() as libc::socklen_t); + if len == 0 { // When there is a datagram from unnamed unix socket // linux returns zero bytes of address diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 9c3119e787b33..4014767461389 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -52,6 +52,25 @@ fn sock_addr_without_trailing_nul() { assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); } +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn sock_addr_pathname_fills_sun_path() { + use crate::ffi::OsStr; + use crate::os::unix::ffi::OsStrExt; + + let mut addr: libc::sockaddr_un = unsafe { crate::mem::zeroed() }; + addr.sun_family = libc::AF_UNIX as libc::sa_family_t; + let mut path = vec![b'a'; addr.sun_path.len()]; + path[0] = b'/'; + for (dst, &src) in addr.sun_path.iter_mut().zip(&path) { + *dst = src as _; + } + let offset = crate::mem::offset_of!(libc::sockaddr_un, sun_path); + + let address = or_panic!(SocketAddr::from_parts(addr, (offset + path.len() + 1) as _)); + assert_eq!(address.as_pathname(), Some(Path::new(OsStr::from_bytes(&path)))); +} + #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets #[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")] diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 21560638c1d0f..3e6a934f318b2 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -50,6 +50,69 @@ pub trait FileExt { #[stable(feature = "file_offset", since = "1.15.0")] fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result; + /// Seeks to a given position and reads the exact number of bytes required to fill `buf`. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the read. + /// + /// Similar to [`io::Read::read_exact`] but uses [`seek_read`] instead of `read`. + /// + /// [`seek_read`]: FileExt::seek_read + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`io::ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`io::ErrorKind::UnexpectedEof`]. + /// The contents of `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately + /// returns. The contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it + /// has read, but it will never read more than would be necessary to + /// completely fill the buffer. + /// + /// # Examples + /// + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] + /// #![feature(seek_read_exact_seek_write_all)] + /// + /// use std::io; + /// use std::fs::File; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// let mut buffer = [0; 10]; + /// + /// // Read 10 bytes, starting 72 bytes from the + /// // start of the file. + /// file.seek_read_exact(&mut buffer[..], 72)?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_read_exact_seek_write_all", issue = "162868")] + fn seek_read_exact(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.seek_read(buf, offset) { + Ok(0) => break, + Ok(n) => { + buf = &mut buf[n..]; + offset += n as u64; + } + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } + } + /// Seeks to a given position and reads some bytes into the buffer. /// /// This is equivalent to the [`seek_read`](FileExt::seek_read) method, except that it is passed @@ -122,6 +185,62 @@ pub trait FileExt { /// ``` #[stable(feature = "file_offset", since = "1.15.0")] fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result; + + /// Seeks to a given position and attempts to write an entire buffer. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the write. + /// + /// This method will continuously call [`seek_write`] until there is no more data + /// to be written or an error of non-[`io::ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`io::ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`io::ErrorKind::Interrupted`] kind that [`seek_write`] returns. + /// + /// [`seek_write`]: FileExt::seek_write + /// + /// # Examples + /// + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] + /// #![feature(seek_read_exact_seek_write_all)] + /// + /// use std::fs::File; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Write a byte string starting 72 bytes from + /// // the start of the file. + /// buffer.seek_write_all(b"some bytes", 72)?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_read_exact_seek_write_all", issue = "162868")] + fn seek_write_all(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.seek_write(buf, offset) { + Ok(0) => { + return Err(io::Error::WRITE_ALL_EOF); + } + Ok(n) => { + buf = &buf[n..]; + offset += n as u64 + } + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + Ok(()) + } } #[stable(feature = "file_offset", since = "1.15.0")] diff --git a/library/std/src/sys/fs/common.rs b/library/std/src/sys/fs/common.rs index 17b98a4506544..96bafb26bb969 100644 --- a/library/std/src/sys/fs/common.rs +++ b/library/std/src/sys/fs/common.rs @@ -77,6 +77,10 @@ impl Dir { Self::open(path, &opts) } + pub fn duplicate(&self) -> io::Result { + Ok(Self { path: self.path.clone() }) + } + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { File::open(&self.path.join(path), opts) } diff --git a/library/std/src/sys/fs/unix/dir.rs b/library/std/src/sys/fs/unix/dir.rs index 3fe952d942927..cf0dece265054 100644 --- a/library/std/src/sys/fs/unix/dir.rs +++ b/library/std/src/sys/fs/unix/dir.rs @@ -47,6 +47,10 @@ impl Dir { run_path_with_cstr(path, &|path| Self::open_traversal_c(path)) } + pub fn duplicate(&self) -> io::Result { + Ok(Self(self.0.try_clone()?)) + } + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, opts, 0)) .map(FileDesc::from_inner) diff --git a/library/std/src/sys/fs/windows/dir.rs b/library/std/src/sys/fs/windows/dir.rs index 70bade84f58fd..d4674ad24f87e 100644 --- a/library/std/src/sys/fs/windows/dir.rs +++ b/library/std/src/sys/fs/windows/dir.rs @@ -72,6 +72,10 @@ impl Dir { with_native_path(path, &|path| Self::open_with_native(path, &opts)) } + pub fn duplicate(&self) -> io::Result { + Ok(Self { handle: self.handle.try_clone()? }) + } + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { // NtCreateFile will fail if given an absolute path and a non-null RootDirectory if path.is_absolute() { diff --git a/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs b/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs index 070ffaa1eff00..99d63929db484 100644 --- a/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs +++ b/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs @@ -1,18 +1,42 @@ +// Test that we can recover from very basic C arrays in the parser & provide a good diagnostic. + fn main() {} -const FOO: [u8; 3] = { +const INTS: [u8; 3] = { //~^ ERROR this is a block expression, not an array 1, 2, 3 }; -const BAR: [&str; 3] = {"one", "two", "three"}; +const STRS: [&str; 3] = {"one", "two", "three"}; //~^ ERROR this is a block expression, not an array -fn foo() { +fn expr_stmt() { {1, 2, 3}; //~^ ERROR this is a block expression, not an array } -fn bar() { +// Don't trigger here. +fn unsafe_block() { + unsafe { 1, 2, 3 } //~ ERROR expected one of +} + +// Don't trigger here. +fn labeled_block() { + 'label: { 1, 2, 3 } //~ ERROR expected one of +} + +// Don't trigger here, this is not a block expression, only a block. +fn fn_body_block() { 1, 2, 3 //~ ERROR expected one of } + +// Don't trigger here, this is not a block expression, only a block. +fn closure_body_block() { + || -> i32 { 1, 2, 3 }; //~ ERROR expected one of +} + +// Don't trigger here. +fn const_arg() { + struct Casket; + Casket::<{ 1, 2, 3 }>; //~ ERROR expected one of +} diff --git a/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr b/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr index 58232e2307d8e..531d54a9e3f1e 100644 --- a/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr +++ b/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr @@ -1,8 +1,8 @@ error: this is a block expression, not an array - --> $DIR/issue-87830-try-brackets-for-arrays.rs:3:22 + --> $DIR/issue-87830-try-brackets-for-arrays.rs:5:23 | -LL | const FOO: [u8; 3] = { - | ______________________^ +LL | const INTS: [u8; 3] = { + | _______________________^ LL | | LL | | 1, 2, 3 LL | | }; @@ -10,26 +10,26 @@ LL | | }; | help: to make an array, use square brackets instead of curly braces | -LL ~ const FOO: [u8; 3] = [ +LL ~ const INTS: [u8; 3] = [ LL | LL | 1, 2, 3 LL ~ ]; | error: this is a block expression, not an array - --> $DIR/issue-87830-try-brackets-for-arrays.rs:8:24 + --> $DIR/issue-87830-try-brackets-for-arrays.rs:10:25 | -LL | const BAR: [&str; 3] = {"one", "two", "three"}; - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | const STRS: [&str; 3] = {"one", "two", "three"}; + | ^^^^^^^^^^^^^^^^^^^^^^^ | help: to make an array, use square brackets instead of curly braces | -LL - const BAR: [&str; 3] = {"one", "two", "three"}; -LL + const BAR: [&str; 3] = ["one", "two", "three"]; +LL - const STRS: [&str; 3] = {"one", "two", "three"}; +LL + const STRS: [&str; 3] = ["one", "two", "three"]; | error: this is a block expression, not an array - --> $DIR/issue-87830-try-brackets-for-arrays.rs:12:5 + --> $DIR/issue-87830-try-brackets-for-arrays.rs:14:5 | LL | {1, 2, 3}; | ^^^^^^^^^ @@ -41,10 +41,34 @@ LL + [1, 2, 3]; | error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,` - --> $DIR/issue-87830-try-brackets-for-arrays.rs:17:6 + --> $DIR/issue-87830-try-brackets-for-arrays.rs:20:15 + | +LL | unsafe { 1, 2, 3 } + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,` + --> $DIR/issue-87830-try-brackets-for-arrays.rs:25:16 + | +LL | 'label: { 1, 2, 3 } + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,` + --> $DIR/issue-87830-try-brackets-for-arrays.rs:30:6 | LL | 1, 2, 3 | ^ expected one of `.`, `;`, `?`, `}`, or an operator -error: aborting due to 4 previous errors +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,` + --> $DIR/issue-87830-try-brackets-for-arrays.rs:35:18 + | +LL | || -> i32 { 1, 2, 3 }; + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,` + --> $DIR/issue-87830-try-brackets-for-arrays.rs:41:17 + | +LL | Casket::<{ 1, 2, 3 }>; + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: aborting due to 8 previous errors diff --git a/tests/ui/fn/fn-item-type-note-late-bound-145558.rs b/tests/ui/fn/fn-item-type-note-late-bound-145558.rs new file mode 100644 index 0000000000000..3e8fc0e8b14a7 --- /dev/null +++ b/tests/ui/fn/fn-item-type-note-late-bound-145558.rs @@ -0,0 +1,20 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/145558 +//! +//! The note explaining that distinct fn items have distinct types was suppressed when the +//! signatures contained a late-bound lifetime, because the two binders name their bound +//! region differently. + +//@ dont-require-annotations: NOTE + +struct A; + +fn f1<'a>(_: &'a A) {} +fn f2<'a>(_: &'a A) {} + +fn main() { + let mut map = vec![]; + map.push(f1); + map.push(f2); + //~^ ERROR mismatched types + //~| NOTE different fn items have unique types +} diff --git a/tests/ui/fn/fn-item-type-note-late-bound-145558.stderr b/tests/ui/fn/fn-item-type-note-late-bound-145558.stderr new file mode 100644 index 0000000000000..bf0a1c3316e57 --- /dev/null +++ b/tests/ui/fn/fn-item-type-note-late-bound-145558.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/fn-item-type-note-late-bound-145558.rs:17:14 + | +LL | map.push(f1); + | --- -- this argument has type `for<'a> fn(&'a A) {f1}`... + | | + | ... which causes `map` to have type `Vec fn(&'a A) {f1}>` +LL | map.push(f2); + | ---- ^^ expected fn item, found a different fn item + | | + | arguments to this method are incorrect + | + = note: expected fn item `for<'a> fn(&'a A) {f1}` + found fn item `for<'a> fn(&'a A) {f2}` + = note: different fn items have unique types, even if their signatures are the same +note: method defined here + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/let-else/detect-invisible-delimiter.rs b/tests/ui/let-else/detect-invisible-delimiter.rs new file mode 100644 index 0000000000000..76c231bd21e6d --- /dev/null +++ b/tests/ui/let-else/detect-invisible-delimiter.rs @@ -0,0 +1,16 @@ +// The user shouldn't need to wrap the expression in parentheses(#147899) +//@check-pass +#![allow(irrefutable_let_patterns)] +struct Thing {} +macro_rules! foo { + ($e:expr) => { + let _ = $e else { + return; + }; + }; +} + +fn main() { + foo!(true && true); + foo!(Thing {}); +} diff --git a/tests/ui/lint/lint-const-item-mutation.rs b/tests/ui/lint/lint-const-item-mutation.rs index d51d3c394937c..877455e7bb869 100644 --- a/tests/ui/lint/lint-const-item-mutation.rs +++ b/tests/ui/lint/lint-const-item-mutation.rs @@ -18,16 +18,19 @@ impl Drop for Mutable { } } -struct Mutable2 { // this one has drop glue but not a Drop impl +struct Mutable2 { // this one has drop glue but not a direct Drop impl msg: &'static str, other: String, } +struct WithFieldDrop { inner: Mutable } // no Drop on this type, but Mutable has one + const ARRAY: [u8; 1] = [25]; const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; const RAW_PTR: *mut u8 = 1 as *mut u8; const MUTABLE: Mutable = Mutable { msg: "" }; const MUTABLE2: Mutable2 = Mutable2 { msg: "", other: String::new() }; +const WFD: WithFieldDrop = WithFieldDrop { inner: Mutable { msg: "" } }; const VEC: Vec = Vec::new(); const PTR: *mut () = 1 as *mut _; const PTR_TO_ARRAY: *mut [u32; 4] = 0x12345678 as _; @@ -50,8 +53,9 @@ fn main() { *MY_STRUCT.raw_ptr = 0; } - MUTABLE.msg = "wow"; // no warning, because Drop observes the mutation - MUTABLE2.msg = "wow"; //~ WARN attempting to modify + MUTABLE.msg = "wow"; // no warning — Drop impl observes the mutation + MUTABLE2.msg = "wow"; // no warning — field String has drop glue (needs_drop = true) + WFD.inner.msg = "observed"; // no warning — Mutable's Drop observes the field mutation VEC.push(0); //~ WARN taking a mutable reference to a `const` item // Test that we don't warn when converting a raw pointer diff --git a/tests/ui/lint/lint-const-item-mutation.stderr b/tests/ui/lint/lint-const-item-mutation.stderr index 0e405c306fe46..84f5e78953e60 100644 --- a/tests/ui/lint/lint-const-item-mutation.stderr +++ b/tests/ui/lint/lint-const-item-mutation.stderr @@ -1,45 +1,45 @@ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:37:5 + --> $DIR/lint-const-item-mutation.rs:40:5 | LL | ARRAY[0] = 5; | ^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary; the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:26:1 + --> $DIR/lint-const-item-mutation.rs:28:1 | LL | const ARRAY: [u8; 1] = [25]; | ^^^^^^^^^^^^^^^^^^^^ = note: `#[warn(const_item_mutation)]` on by default warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:38:5 + --> $DIR/lint-const-item-mutation.rs:41:5 | LL | MY_STRUCT.field = false; | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary; the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:27:1 + --> $DIR/lint-const-item-mutation.rs:29:1 | LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:39:5 + --> $DIR/lint-const-item-mutation.rs:42:5 | LL | MY_STRUCT.inner_array[0] = 'b'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary; the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:27:1 + --> $DIR/lint-const-item-mutation.rs:29:1 | LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:40:5 + --> $DIR/lint-const-item-mutation.rs:43:5 | LL | MY_STRUCT.use_mut(); | ^^^^^^^^^^^^^^^^^^^ @@ -52,13 +52,13 @@ note: mutable reference created due to call to this method LL | fn use_mut(&mut self) {} | ^^^^^^^^^^^^^^^^^^^^^ note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:27:1 + --> $DIR/lint-const-item-mutation.rs:29:1 | LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:41:5 + --> $DIR/lint-const-item-mutation.rs:44:5 | LL | &mut MY_STRUCT; | ^^^^^^^^^^^^^^ @@ -66,13 +66,13 @@ LL | &mut MY_STRUCT; = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:27:1 + --> $DIR/lint-const-item-mutation.rs:29:1 | LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:42:5 + --> $DIR/lint-const-item-mutation.rs:45:5 | LL | (&mut MY_STRUCT).use_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -85,26 +85,13 @@ note: mutable reference created due to call to this method LL | fn use_mut(&mut self) {} | ^^^^^^^^^^^^^^^^^^^^^ note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:27:1 + --> $DIR/lint-const-item-mutation.rs:29:1 | LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:54:5 - | -LL | MUTABLE2.msg = "wow"; - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: each usage of a `const` item creates a new temporary; the original `const` item will not be modified -note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:30:1 - | -LL | const MUTABLE2: Mutable2 = Mutable2 { msg: "", other: String::new() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:55:5 + --> $DIR/lint-const-item-mutation.rs:59:5 | LL | VEC.push(0); | ^^^^^^^^^^^ @@ -114,10 +101,10 @@ LL | VEC.push(0); note: mutable reference created due to call to this method --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:31:1 + --> $DIR/lint-const-item-mutation.rs:34:1 | LL | const VEC: Vec = Vec::new(); | ^^^^^^^^^^^^^^^^^^^ -warning: 8 warnings emitted +warning: 7 warnings emitted diff --git a/tests/ui/parser/issues/true-false-type-issue-162947.fixed b/tests/ui/parser/issues/true-false-type-issue-162947.fixed new file mode 100644 index 0000000000000..8d3037ea0f458 --- /dev/null +++ b/tests/ui/parser/issues/true-false-type-issue-162947.fixed @@ -0,0 +1,17 @@ +//@ run-rustfix + +struct A; + +impl A { + fn _a() -> bool { //~ ERROR: expected type, found keyword `true` + false + } + fn b(&self) {} +} + +fn main() { + let a = A; + a.b(); + + let _b: bool = true; //~ ERROR: expected type, found keyword `true` +} diff --git a/tests/ui/parser/issues/true-false-type-issue-162947.rs b/tests/ui/parser/issues/true-false-type-issue-162947.rs new file mode 100644 index 0000000000000..1c3a35b3f16f1 --- /dev/null +++ b/tests/ui/parser/issues/true-false-type-issue-162947.rs @@ -0,0 +1,17 @@ +//@ run-rustfix + +struct A; + +impl A { + fn _a() -> true { //~ ERROR: expected type, found keyword `true` + false + } + fn b(&self) {} +} + +fn main() { + let a = A; + a.b(); + + let _b: true = true; //~ ERROR: expected type, found keyword `true` +} diff --git a/tests/ui/parser/issues/true-false-type-issue-162947.stderr b/tests/ui/parser/issues/true-false-type-issue-162947.stderr new file mode 100644 index 0000000000000..558587d8ebe88 --- /dev/null +++ b/tests/ui/parser/issues/true-false-type-issue-162947.stderr @@ -0,0 +1,20 @@ +error: expected type, found keyword `true` + --> $DIR/true-false-type-issue-162947.rs:6:16 + | +LL | fn _a() -> true { + | ^^^^ + | | + | expected type + | help: the type is called: `bool` + +error: expected type, found keyword `true` + --> $DIR/true-false-type-issue-162947.rs:16:13 + | +LL | let _b: true = true; + | ^^^^ + | | + | expected type + | help: the type is called: `bool` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/let-else-fullwidth-brace.rs b/tests/ui/parser/let-else-fullwidth-brace.rs new file mode 100644 index 0000000000000..98bd65605faa8 --- /dev/null +++ b/tests/ui/parser/let-else-fullwidth-brace.rs @@ -0,0 +1,7 @@ +#![allow(irrefutable_let_patterns)] + +fn main() { + let x = {1} else { return; }; + //~^ ERROR unknown start of token: \u{ff5d} + //~| ERROR right curly brace `}` before `else` in a `let...else` statement not allowed +} diff --git a/tests/ui/parser/let-else-fullwidth-brace.stderr b/tests/ui/parser/let-else-fullwidth-brace.stderr new file mode 100644 index 0000000000000..a5bb4d029dbc5 --- /dev/null +++ b/tests/ui/parser/let-else-fullwidth-brace.stderr @@ -0,0 +1,25 @@ +error: unknown start of token: \u{ff5d} + --> $DIR/let-else-fullwidth-brace.rs:4:15 + | +LL | let x = {1} else { return; }; + | ^^ + | +help: Unicode character '}' (Fullwidth Right Curly Bracket) looks like '}' (Right Curly Brace), but it is not + | +LL - let x = {1} else { return; }; +LL + let x = {1} else { return; }; + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/let-else-fullwidth-brace.rs:4:15 + | +LL | let x = {1} else { return; }; + | ^^ + | +help: wrap the expression in parentheses + | +LL | let x = ({1}) else { return; }; + | + + + +error: aborting due to 2 previous errors + diff --git a/tests/ui/structs/invalid-self-constructor-56835.stderr b/tests/ui/structs/invalid-self-constructor-56835.stderr index 045781ec42bd2..9b25348d87d39 100644 --- a/tests/ui/structs/invalid-self-constructor-56835.stderr +++ b/tests/ui/structs/invalid-self-constructor-56835.stderr @@ -2,7 +2,13 @@ error: the `Self` constructor can only be used with tuple or unit structs --> $DIR/invalid-self-constructor-56835.rs:5:12 | LL | fn bar(Self(foo): Self) {} - | ^^^^^^^^^ help: use curly brackets: `Self { /* fields */ }` + | ^^^^^^^^^ + | +help: use curly brackets + | +LL - fn bar(Self(foo): Self) {} +LL + fn bar(Self { /* fields */ }: Self) {} + | error[E0164]: expected tuple struct or tuple variant, found self constructor `Self` --> $DIR/invalid-self-constructor-56835.rs:5:12 diff --git a/tests/ui/typeck/self-constructor-type-error-56199.rs b/tests/ui/typeck/self-constructor-type-error-56199.rs index b08d69189807a..34af8bed25c47 100644 --- a/tests/ui/typeck/self-constructor-type-error-56199.rs +++ b/tests/ui/typeck/self-constructor-type-error-56199.rs @@ -1,5 +1,8 @@ // https://github.com/rust-lang/rust/issues/56199 enum Foo {} +enum Lab { + Qux, +} struct Bar {} impl Foo { @@ -9,6 +12,12 @@ impl Foo { let _ = Self(); //~^ ERROR the `Self` constructor can only be used with tuple or unit structs } + fn foo_method(self) { + let _ = Self; + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + let _ = Self(); + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + } } impl Bar { @@ -18,6 +27,28 @@ impl Bar { let _ = Self(); //~^ ERROR the `Self` constructor can only be used with tuple or unit structs } + fn bar_method(self) { + let _ = Self; + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + let _ = Self(); + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + } +} + +impl Lab { + fn lab() { + let _ = Self; + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + let _ = Self(); + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + } + fn lab_method(self) { + let _ = Self; + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + let _ = Self(); + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + } } + fn main() {} diff --git a/tests/ui/typeck/self-constructor-type-error-56199.stderr b/tests/ui/typeck/self-constructor-type-error-56199.stderr index 6e9d0fcd90c05..d0d124c6f3149 100644 --- a/tests/ui/typeck/self-constructor-type-error-56199.stderr +++ b/tests/ui/typeck/self-constructor-type-error-56199.stderr @@ -1,30 +1,145 @@ error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/self-constructor-type-error-56199.rs:7:17 + --> $DIR/self-constructor-type-error-56199.rs:10:17 | LL | let _ = Self; | ^^^^ | - = help: did you mean to use one of the enum's variants? +help: the enum is unconstructable because it has no variants + --> $DIR/self-constructor-type-error-56199.rs:2:1 + | +LL | enum Foo {} + | ^^^^^^^^ error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/self-constructor-type-error-56199.rs:9:17 + --> $DIR/self-constructor-type-error-56199.rs:12:17 | LL | let _ = Self(); | ^^^^^^ | - = help: did you mean to use one of the enum's variants? +help: the enum is unconstructable because it has no variants + --> $DIR/self-constructor-type-error-56199.rs:2:1 + | +LL | enum Foo {} + | ^^^^^^^^ error: the `Self` constructor can only be used with tuple or unit structs --> $DIR/self-constructor-type-error-56199.rs:16:17 | LL | let _ = Self; - | ^^^^ help: use curly brackets: `Self { /* fields */ }` + | ^^^^ + | +help: you might have meant to refer to the `self` binding of type `Foo` (notice the capitalization) + | +LL - let _ = Self; +LL + let _ = self; + | error: the `Self` constructor can only be used with tuple or unit structs --> $DIR/self-constructor-type-error-56199.rs:18:17 | LL | let _ = Self(); - | ^^^^^^ help: use curly brackets: `Self { /* fields */ }` + | ^^^^^^ + | +help: the enum is unconstructable because it has no variants + --> $DIR/self-constructor-type-error-56199.rs:2:1 + | +LL | enum Foo {} + | ^^^^^^^^ + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:25:17 + | +LL | let _ = Self; + | ^^^^ + | +help: use curly brackets + | +LL | let _ = Self { /* fields */ }; + | ++++++++++++++++ + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:27:17 + | +LL | let _ = Self(); + | ^^^^^^ + | +help: use curly brackets + | +LL - let _ = Self(); +LL + let _ = Self { /* fields */ }; + | + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:31:17 + | +LL | let _ = Self; + | ^^^^ + | +help: you might have meant to refer to the `self` binding of type `Bar` (notice the capitalization) + | +LL - let _ = Self; +LL + let _ = self; + | + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:33:17 + | +LL | let _ = Self(); + | ^^^^^^ + | +help: use curly brackets + | +LL - let _ = Self(); +LL + let _ = Self { /* fields */ }; + | + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:40:17 + | +LL | let _ = Self; + | ^^^^ + | +help: you might have meant to use one of the enum's variants + --> $DIR/self-constructor-type-error-56199.rs:3:1 + | +LL | enum Lab { + | ^^^^^^^^ + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:42:17 + | +LL | let _ = Self(); + | ^^^^^^ + | +help: you might have meant to use one of the enum's variants + --> $DIR/self-constructor-type-error-56199.rs:3:1 + | +LL | enum Lab { + | ^^^^^^^^ + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:46:17 + | +LL | let _ = Self; + | ^^^^ + | +help: you might have meant to refer to the `self` binding of type `Lab` (notice the capitalization) + | +LL - let _ = Self; +LL + let _ = self; + | + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:48:17 + | +LL | let _ = Self(); + | ^^^^^^ + | +help: you might have meant to use one of the enum's variants + --> $DIR/self-constructor-type-error-56199.rs:3:1 + | +LL | enum Lab { + | ^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 12 previous errors diff --git a/triagebot.toml b/triagebot.toml index 475cf6e044f9b..60098a038ea73 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -702,6 +702,7 @@ topic = "#{number}: {title}" message_on_add = """\ @*T-types* issue #{number} "{title}" has been nominated for team discussion. """ +github_comment = ":robot: A [dedicated `#t-types/nominated` topic]({zulip_topic_url}) has been opened for humans to discuss this issue :robot:" message_on_remove = "Issue #{number}'s nomination has been removed. Thanks all for participating!" message_on_close = "Issue #{number} has been closed. Thanks for participating!" message_on_reopen = "Issue #{number} has been reopened. Pinging @*T-types*."