diff --git a/Cargo.lock b/Cargo.lock index 16d17cf85836b..3d7f00e6410b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3556,6 +3556,7 @@ dependencies = [ name = "tidy" version = "0.1.0" dependencies = [ + "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.81 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 96aed7293450c..285f9458c44b6 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -436,7 +436,7 @@ fn configure_cmake(builder: &Builder<'_>, } if env::var_os("SCCACHE_ERROR_LOG").is_some() { - cfg.env("RUST_LOG", "sccache=warn"); + cfg.env("RUSTC_LOG", "sccache=warn"); } } diff --git a/src/librustc/error_codes.rs b/src/librustc/error_codes.rs index 7f68b35d014cb..fd089fc688e32 100644 --- a/src/librustc/error_codes.rs +++ b/src/librustc/error_codes.rs @@ -362,10 +362,6 @@ struct Foo1 { x: &bool } // ^ expected lifetime parameter struct Foo2<'a> { x: &'a bool } // correct -impl Foo2 {} - // ^^^^ expected lifetime parameter -impl<'a> Foo2<'a> {} // correct - struct Bar1 { x: Foo2 } // ^^^^ expected lifetime parameter struct Bar2<'a> { x: Foo2<'a> } // correct @@ -2208,4 +2204,5 @@ register_diagnostics! { E0710, // an unknown tool name found in scoped lint E0711, // a feature has been declared with conflicting stability attributes // E0702, // replaced with a generic attribute input check + E0726, // non-explicit (not `'_`) elided lifetime in unsupported position } diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 6d57882976e11..ad26e05bf80d0 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2110,15 +2110,49 @@ impl<'a> LoweringContext<'a> { .expect("already checked that type args or bindings exist"); (false, first_generic_span.shrink_to_lo(), format!("{}, ", anon_lt_suggestion)) }; - self.sess.buffer_lint_with_diagnostic( - ELIDED_LIFETIMES_IN_PATHS, - CRATE_NODE_ID, - path_span, - "hidden lifetime parameters in types are deprecated", - builtin::BuiltinLintDiagnostics::ElidedLifetimesInPaths( - expected_lifetimes, path_span, incl_angl_brckt, insertion_span, suggestion - ) - ); + match self.anonymous_lifetime_mode { + // In create-parameter mode we error here because we don't want to support + // deprecated impl elision in new features like impl elision and `async fn`, + // both of which work using the `CreateParameter` mode: + // + // impl Foo for std::cell::Ref // note lack of '_ + // async fn foo(_: std::cell::Ref) { ... } + AnonymousLifetimeMode::CreateParameter => { + let mut err = struct_span_err!( + self.sess, + path_span, + E0726, + "implicit elided lifetime not allowed here" + ); + crate::lint::builtin::add_elided_lifetime_in_path_suggestion( + &self.sess, + &mut err, + expected_lifetimes, + path_span, + incl_angl_brckt, + insertion_span, + suggestion, + ); + err.emit(); + } + AnonymousLifetimeMode::PassThrough | + AnonymousLifetimeMode::ReportError | + AnonymousLifetimeMode::Replace(_) => { + self.sess.buffer_lint_with_diagnostic( + ELIDED_LIFETIMES_IN_PATHS, + CRATE_NODE_ID, + path_span, + "hidden lifetime parameters in types are deprecated", + builtin::BuiltinLintDiagnostics::ElidedLifetimesInPaths( + expected_lifetimes, + path_span, + incl_angl_brckt, + insertion_span, + suggestion, + ) + ); + } + } } } @@ -5335,13 +5369,15 @@ impl<'a> LoweringContext<'a> { fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime { match self.anonymous_lifetime_mode { - // N.B., We intentionally ignore the create-parameter mode here - // and instead "pass through" to resolve-lifetimes, which will then - // report an error. This is because we don't want to support - // impl elision for deprecated forms like - // - // impl Foo for std::cell::Ref // note lack of '_ - AnonymousLifetimeMode::CreateParameter | + AnonymousLifetimeMode::CreateParameter => { + // We should have emitted E0726 when processing this path above + self.sess.delay_span_bug( + span, + "expected 'implicit elided lifetime not allowed' error", + ); + let id = self.sess.next_node_id(); + self.new_named_lifetime(id, span, hir::LifetimeName::Error) + } // This is the normal case. AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span), diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 5a2807ac93d85..4293019aed568 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1927,6 +1927,9 @@ pub enum ArgSource { /// Represents the header (not the body) of a function declaration. #[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)] pub struct FnDecl { + /// The types of the function's arguments. + /// + /// Additional argument data is stored in the function's [body](Body::arguments). pub inputs: HirVec, pub output: FunctionRetTy, pub c_variadic: bool, diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index c20a08fc5aea6..95b566d4a1b69 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -644,7 +644,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { for sp in prior_arms { err.span_label(*sp, format!( "this is found to be of type `{}`", - last_ty, + self.resolve_type_vars_if_possible(&last_ty), )); } } else if let Some(sp) = prior_arms.last() { diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 65bc2b3e945ce..a8f39e441309a 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -704,7 +704,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// potentially leaving "dangling type variables" behind. /// In such cases, an assertion will fail when attempting to /// register obligations, within a snapshot. Very useful, much - /// better than grovelling through megabytes of `RUST_LOG` output. + /// better than grovelling through megabytes of `RUSTC_LOG` output. /// /// HOWEVER, in some cases the flag is unhelpful. In particular, we /// sometimes create a "mini-fulfilment-cx" in which we enroll diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 28a2a1eaf6b49..993e1aacb4e4d 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -376,7 +376,7 @@ declare_lint! { declare_lint! { pub AMBIGUOUS_ASSOCIATED_ITEMS, - Warn, + Deny, "ambiguous associated items" } @@ -477,6 +477,48 @@ pub enum BuiltinLintDiagnostics { RedundantImport(Vec<(Span, bool)>, ast::Ident), } +pub(crate) fn add_elided_lifetime_in_path_suggestion( + sess: &Session, + db: &mut DiagnosticBuilder<'_>, + n: usize, + path_span: Span, + incl_angl_brckt: bool, + insertion_span: Span, + anon_lts: String, +) { + let (replace_span, suggestion) = if incl_angl_brckt { + (insertion_span, anon_lts) + } else { + // When possible, prefer a suggestion that replaces the whole + // `Path` expression with `Path<'_, T>`, rather than inserting `'_, ` + // at a point (which makes for an ugly/confusing label) + if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) { + // But our spans can get out of whack due to macros; if the place we think + // we want to insert `'_` isn't even within the path expression's span, we + // should bail out of making any suggestion rather than panicking on a + // subtract-with-overflow or string-slice-out-out-bounds (!) + // FIXME: can we do better? + if insertion_span.lo().0 < path_span.lo().0 { + return; + } + let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize; + if insertion_index > snippet.len() { + return; + } + let (before, after) = snippet.split_at(insertion_index); + (path_span, format!("{}{}{}", before, anon_lts, after)) + } else { + (insertion_span, anon_lts) + } + }; + db.span_suggestion( + replace_span, + &format!("indicate the anonymous lifetime{}", if n >= 2 { "s" } else { "" }), + suggestion, + Applicability::MachineApplicable + ); +} + impl BuiltinLintDiagnostics { pub fn run(self, sess: &Session, db: &mut DiagnosticBuilder<'_>) { match self { @@ -521,36 +563,14 @@ impl BuiltinLintDiagnostics { BuiltinLintDiagnostics::ElidedLifetimesInPaths( n, path_span, incl_angl_brckt, insertion_span, anon_lts ) => { - let (replace_span, suggestion) = if incl_angl_brckt { - (insertion_span, anon_lts) - } else { - // When possible, prefer a suggestion that replaces the whole - // `Path` expression with `Path<'_, T>`, rather than inserting `'_, ` - // at a point (which makes for an ugly/confusing label) - if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) { - // But our spans can get out of whack due to macros; if the place we think - // we want to insert `'_` isn't even within the path expression's span, we - // should bail out of making any suggestion rather than panicking on a - // subtract-with-overflow or string-slice-out-out-bounds (!) - // FIXME: can we do better? - if insertion_span.lo().0 < path_span.lo().0 { - return; - } - let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize; - if insertion_index > snippet.len() { - return; - } - let (before, after) = snippet.split_at(insertion_index); - (path_span, format!("{}{}{}", before, anon_lts, after)) - } else { - (insertion_span, anon_lts) - } - }; - db.span_suggestion( - replace_span, - &format!("indicate the anonymous lifetime{}", if n >= 2 { "s" } else { "" }), - suggestion, - Applicability::MachineApplicable + add_elided_lifetime_in_path_suggestion( + sess, + db, + n, + path_span, + incl_angl_brckt, + insertion_span, + anon_lts, ); } BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => { diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 8d5c1798e0fa4..e713cf8d80553 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -757,12 +757,12 @@ impl<'a, 'tcx> LateContext<'a, 'tcx> { /// Check if a `DefId`'s path matches the given absolute type path usage. /// /// # Examples - /// ```rust,ignore (no `cx` or `def_id` available) + /// + /// ```rust,ignore (no context or def id available) /// if cx.match_def_path(def_id, &["core", "option", "Option"]) { /// // The given `def_id` is that of an `Option` type /// } /// ``` - // Uplifted from rust-lang/rust-clippy pub fn match_def_path(&self, def_id: DefId, path: &[&str]) -> bool { let names = self.get_def_path(def_id); @@ -772,13 +772,13 @@ impl<'a, 'tcx> LateContext<'a, 'tcx> { /// Gets the absolute path of `def_id` as a vector of `&str`. /// /// # Examples - /// ```rust,ignore (no `cx` or `def_id` available) + /// + /// ```rust,ignore (no context or def id available) /// let def_path = cx.get_def_path(def_id); /// if let &["core", "option", "Option"] = &def_path[..] { /// // The given `def_id` is that of an `Option` type /// } /// ``` - // Uplifted from rust-lang/rust-clippy pub fn get_def_path(&self, def_id: DefId) -> Vec { pub struct AbsolutePathPrinter<'a, 'tcx> { pub tcx: TyCtxt<'a, 'tcx, 'tcx>, diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 765992fd2b70e..0d67865d71708 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -784,26 +784,30 @@ pub fn file_metadata(cx: &CodegenCx<'ll, '_>, file_name, defining_crate); - let directory = if defining_crate == LOCAL_CRATE { - &cx.sess().working_dir.0 + let file_name = &file_name.to_string(); + let file_name_symbol = Symbol::intern(file_name); + if defining_crate == LOCAL_CRATE { + let directory = &cx.sess().working_dir.0.to_string_lossy(); + file_metadata_raw(cx, file_name, Some(file_name_symbol), + directory, Some(Symbol::intern(directory))) } else { // If the path comes from an upstream crate we assume it has been made // independent of the compiler's working directory one way or another. - Path::new("") - }; - - file_metadata_raw(cx, &file_name.to_string(), &directory.to_string_lossy()) + file_metadata_raw(cx, file_name, Some(file_name_symbol), "", None) + } } pub fn unknown_file_metadata(cx: &CodegenCx<'ll, '_>) -> &'ll DIFile { - file_metadata_raw(cx, "", "") + file_metadata_raw(cx, "", None, "", None) } fn file_metadata_raw(cx: &CodegenCx<'ll, '_>, file_name: &str, - directory: &str) + file_name_symbol: Option, + directory: &str, + directory_symbol: Option) -> &'ll DIFile { - let key = (Symbol::intern(file_name), Symbol::intern(directory)); + let key = (file_name_symbol, directory_symbol); if let Some(file_metadata) = debug_context(cx).created_files.borrow().get(&key) { return *file_metadata; diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs index 2154ac9b1d259..f3070a03b4ed5 100644 --- a/src/librustc_codegen_llvm/debuginfo/mod.rs +++ b/src/librustc_codegen_llvm/debuginfo/mod.rs @@ -63,7 +63,7 @@ pub struct CrateDebugContext<'a, 'tcx> { llcontext: &'a llvm::Context, llmod: &'a llvm::Module, builder: &'a mut DIBuilder<'a>, - created_files: RefCell>, + created_files: RefCell, Option), &'a DIFile>>, created_enum_disr_types: RefCell>, type_map: RefCell>, diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 6a5accc8d7f05..5b42b049b5b18 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -1163,7 +1163,7 @@ pub fn report_ices_to_stderr_if_any R, R>(f: F) -> Result = (OpTy<'tcx>, Span); +type Const<'tcx> = OpTy<'tcx>; /// Finds optimization opportunities on the MIR. struct ConstPropagator<'a, 'mir, 'tcx:'a+'mir> { ecx: InterpretCx<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>, - mir: &'mir Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource<'tcx>, places: IndexVec>>, can_const_prop: IndexVec, param_env: ParamEnv<'tcx>, + source_scope_local_data: ClearCrossCrate>, + local_decls: IndexVec>, + promoted: IndexVec>, } impl<'a, 'b, 'tcx> LayoutOf for ConstPropagator<'a, 'b, 'tcx> { @@ -104,20 +117,33 @@ impl<'a, 'b, 'tcx> HasTyCtxt<'tcx> for ConstPropagator<'a, 'b, 'tcx> { impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { fn new( - mir: &'mir Mir<'tcx>, + mir: &mut Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource<'tcx>, ) -> ConstPropagator<'a, 'mir, 'tcx> { let param_env = tcx.param_env(source.def_id()); let ecx = mk_eval_cx(tcx, tcx.def_span(source.def_id()), param_env); + let can_const_prop = CanConstProp::check(mir); + let source_scope_local_data = std::mem::replace( + &mut mir.source_scope_local_data, + ClearCrossCrate::Clear + ); + let promoted = std::mem::replace( + &mut mir.promoted, + IndexVec::new() + ); + ConstPropagator { ecx, - mir, tcx, source, param_env, - can_const_prop: CanConstProp::check(mir), + can_const_prop, places: IndexVec::from_elem(None, &mir.local_decls), + source_scope_local_data, + //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_mir()` needs it + local_decls: mir.local_decls.clone(), + promoted, } } @@ -130,7 +156,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { F: FnOnce(&mut Self) -> EvalResult<'tcx, T>, { self.ecx.tcx.span = source_info.span; - let lint_root = match self.mir.source_scope_local_data { + let lint_root = match self.source_scope_local_data { ClearCrossCrate::Set(ref ivs) => { //FIXME(#51314): remove this check if source_info.scope.index() >= ivs.len() { @@ -252,12 +278,11 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { fn eval_constant( &mut self, c: &Constant<'tcx>, - source_info: SourceInfo, ) -> Option> { - self.ecx.tcx.span = source_info.span; + self.ecx.tcx.span = c.span; match self.ecx.eval_const_to_op(*c.literal, None) { Ok(op) => { - Some((op, c.span)) + Some(op) }, Err(error) => { let err = error_to_const_error(&self.ecx, error); @@ -273,11 +298,11 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { Place::Projection(ref proj) => match proj.elem { ProjectionElem::Field(field, _) => { trace!("field proj on {:?}", proj.base); - let (base, span) = self.eval_place(&proj.base, source_info)?; + let base = self.eval_place(&proj.base, source_info)?; let res = self.use_ecx(source_info, |this| { this.ecx.operand_field(base, field.index() as u64) })?; - Some((res, span)) + Some(res) }, // We could get more projections by using e.g., `operand_projection`, // but we do not even have the stack frame set up properly so @@ -301,11 +326,11 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { // cannot use `const_eval` here, because that would require having the MIR // for the current function available, but we're producing said MIR right now let res = self.use_ecx(source_info, |this| { - let mir = &this.mir.promoted[promoted]; + let mir = &this.promoted[promoted]; eval_promoted(this.tcx, cid, mir, this.param_env) })?; trace!("evaluated promoted {:?} to {:?}", promoted, res); - Some((res.into(), source_info.span)) + Some(res.into()) }, _ => None, } @@ -313,7 +338,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option> { match *op { - Operand::Constant(ref c) => self.eval_constant(c, source_info), + Operand::Constant(ref c) => self.eval_constant(c), | Operand::Move(ref place) | Operand::Copy(ref place) => self.eval_place(place, source_info), } @@ -337,18 +362,18 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { Rvalue::Discriminant(..) => None, Rvalue::Cast(kind, ref operand, _) => { - let (op, span) = self.eval_operand(operand, source_info)?; + let op = self.eval_operand(operand, source_info)?; self.use_ecx(source_info, |this| { let dest = this.ecx.allocate(place_layout, MemoryKind::Stack); this.ecx.cast(op, kind, dest.into())?; - Ok((dest.into(), span)) + Ok(dest.into()) }) } // FIXME(oli-obk): evaluate static/constant slice lengths Rvalue::Len(_) => None, Rvalue::NullaryOp(NullOp::SizeOf, ty) => { - type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some(( + type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some( ImmTy { imm: Immediate::Scalar( Scalar::Bits { @@ -357,9 +382,8 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { }.into() ), layout: self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?, - }.into(), - span, - ))) + }.into() + )) } Rvalue::UnaryOp(op, ref arg) => { let def_id = if self.tcx.is_closure(self.source.def_id()) { @@ -373,7 +397,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { return None; } - let (arg, _) = self.eval_operand(arg, source_info)?; + let arg = self.eval_operand(arg, source_info)?; let val = self.use_ecx(source_info, |this| { let prim = this.ecx.read_immediate(arg)?; match op { @@ -395,7 +419,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { imm: Immediate::Scalar(val.into()), layout: place_layout, }; - Some((res.into(), span)) + Some(res.into()) } Rvalue::CheckedBinaryOp(op, ref left, ref right) | Rvalue::BinaryOp(op, ref left, ref right) => { @@ -413,20 +437,20 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { } let r = self.use_ecx(source_info, |this| { - this.ecx.read_immediate(right.0) + this.ecx.read_immediate(right) })?; if op == BinOp::Shr || op == BinOp::Shl { - let left_ty = left.ty(self.mir, self.tcx); + let left_ty = left.ty(&self.local_decls, self.tcx); let left_bits = self .tcx .layout_of(self.param_env.and(left_ty)) .unwrap() .size .bits(); - let right_size = right.0.layout.size; + let right_size = right.layout.size; let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size)); if r_bits.ok().map_or(false, |b| b >= left_bits as u128) { - let source_scope_local_data = match self.mir.source_scope_local_data { + let source_scope_local_data = match self.source_scope_local_data { ClearCrossCrate::Set(ref data) => data, ClearCrossCrate::Clear => return None, }; @@ -446,7 +470,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { } let left = self.eval_operand(left, source_info)?; let l = self.use_ecx(source_info, |this| { - this.ecx.read_immediate(left.0) + this.ecx.read_immediate(left) })?; trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); let (val, overflow) = self.use_ecx(source_info, |this| { @@ -469,7 +493,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { imm: val, layout: place_layout, }; - Some((res.into(), span)) + Some(res.into()) }, } } @@ -544,8 +568,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { ) { trace!("visit_constant: {:?}", constant); self.super_constant(constant, location); - let source_info = *self.mir.source_info(location); - self.eval_constant(constant, source_info); + self.eval_constant(constant); } fn visit_statement( @@ -556,7 +579,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { trace!("visit_statement: {:?}", statement); if let StatementKind::Assign(ref place, ref rval) = statement.kind { let place_ty: Ty<'tcx> = place - .ty(&self.mir.local_decls, self.tcx) + .ty(&self.local_decls, self.tcx) .ty; if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) { if let Some(value) = self.const_prop(rval, place_layout, statement.source_info) { @@ -574,18 +597,18 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { self.super_statement(statement, location); } - fn visit_terminator_kind( + fn visit_terminator( &mut self, - kind: &TerminatorKind<'tcx>, + terminator: &Terminator<'tcx>, location: Location, ) { - self.super_terminator_kind(kind, location); - let source_info = *self.mir.source_info(location); - if let TerminatorKind::Assert { expected, msg, cond, .. } = kind { - if let Some(value) = self.eval_operand(cond, source_info) { + self.super_terminator(terminator, location); + let source_info = terminator.source_info;; + if let TerminatorKind::Assert { expected, msg, cond, .. } = &terminator.kind { + if let Some(value) = self.eval_operand(&cond, source_info) { trace!("assertion on {:?} should be {:?}", value, expected); let expected = ScalarMaybeUndef::from(Scalar::from_bool(*expected)); - if expected != self.ecx.read_scalar(value.0).unwrap() { + if expected != self.ecx.read_scalar(value).unwrap() { // poison all places this operand references so that further code // doesn't use the invalid value match cond { @@ -600,12 +623,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { }, Operand::Constant(_) => {} } - let span = self.mir[location.block] - .terminator - .as_ref() - .unwrap() - .source_info - .span; + let span = terminator.source_info.span; let hir_id = self .tcx .hir() @@ -621,7 +639,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { let len = self .eval_operand(len, source_info) .expect("len must be const"); - let len = match self.ecx.read_scalar(len.0) { + let len = match self.ecx.read_scalar(len) { Ok(ScalarMaybeUndef::Scalar(Scalar::Bits { bits, .. })) => bits, @@ -630,7 +648,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { let index = self .eval_operand(index, source_info) .expect("index must be const"); - let index = match self.ecx.read_scalar(index.0) { + let index = match self.ecx.read_scalar(index) { Ok(ScalarMaybeUndef::Scalar(Scalar::Bits { bits, .. })) => bits, diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 9dd8a7050fd28..1f5a6d7914125 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -20,7 +20,7 @@ use syntax::ptr::P; use syntax::visit::{self, Visitor}; use syntax::{span_err, struct_span_err, walk_list}; use syntax_ext::proc_macro_decls::is_proc_macro_attr; -use syntax_pos::Span; +use syntax_pos::{Span, MultiSpan}; use errors::Applicability; use log::debug; @@ -679,6 +679,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> { "unions cannot have zero fields"); } } + ItemKind::Existential(ref bounds, _) => { + if !bounds.iter() + .any(|b| if let GenericBound::Trait(..) = *b { true } else { false }) { + let msp = MultiSpan::from_spans(bounds.iter() + .map(|bound| bound.span()).collect()); + self.err_handler().span_err(msp, "at least one trait must be specified"); + } + } _ => {} } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 8cdfbf5f55ca0..6cc7818d712f6 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -1,8 +1,8 @@ use crate::check::{FnCtxt, Expectation, Diverges, Needs}; use crate::check::coercion::CoerceMany; use crate::util::nodemap::FxHashMap; -use errors::Applicability; -use rustc::hir::{self, PatKind}; +use errors::{Applicability, DiagnosticBuilder}; +use rustc::hir::{self, PatKind, Pat}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::pat_util::EnumerateAndAdjustIterator; use rustc::infer; @@ -377,15 +377,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Look for a case like `fn foo(&foo: u32)` and suggest // `fn foo(foo: &u32)` if let Some(mut err) = err { - if let PatKind::Binding(..) = inner.node { - if let Ok(snippet) = tcx.sess.source_map() - .span_to_snippet(pat.span) - { - err.help(&format!("did you mean `{}: &{}`?", - &snippet[1..], - expected)); - } - } + self.borrow_pat_suggestion(&mut err, &pat, &inner, &expected); err.emit(); } (rptr_ty, inner_ty) @@ -517,6 +509,49 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // subtyping. } + fn borrow_pat_suggestion( + &self, + err: &mut DiagnosticBuilder<'_>, + pat: &Pat, + inner: &Pat, + expected: Ty<'tcx>, + ) { + let tcx = self.tcx; + if let PatKind::Binding(..) = inner.node { + let parent_id = tcx.hir().get_parent_node_by_hir_id(pat.hir_id); + let parent = tcx.hir().get_by_hir_id(parent_id); + debug!("inner {:?} pat {:?} parent {:?}", inner, pat, parent); + match parent { + hir::Node::Item(hir::Item { node: hir::ItemKind::Fn(..), .. }) | + hir::Node::ForeignItem(hir::ForeignItem { + node: hir::ForeignItemKind::Fn(..), .. + }) | + hir::Node::TraitItem(hir::TraitItem { node: hir::TraitItemKind::Method(..), .. }) | + hir::Node::ImplItem(hir::ImplItem { node: hir::ImplItemKind::Method(..), .. }) => { + // this pat is likely an argument + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) { + // FIXME: turn into structured suggestion, will need a span that also + // includes the the arg's type. + err.help(&format!("did you mean `{}: &{}`?", snippet, expected)); + } + } + hir::Node::Expr(hir::Expr { node: hir::ExprKind::Match(..), .. }) | + hir::Node::Pat(_) => { + // rely on match ergonomics or it might be nested `&&pat` + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) { + err.span_suggestion( + pat.span, + "you can probably remove the explicit borrow", + snippet, + Applicability::MaybeIncorrect, + ); + } + } + _ => {} // don't provide suggestions in other cases #55175 + } + } + } + pub fn check_dereferencable(&self, span: Span, expected: Ty<'tcx>, inner: &hir::Pat) -> bool { if let PatKind::Binding(..) = inner.node { if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) { diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 849298698af2c..5c0a4da1cd7ef 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -166,9 +166,18 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize, compile_fail: bool, mut error_codes: Vec, opts: &TestOptions, maybe_sysroot: Option, linker: Option, edition: Edition, persist_doctests: Option) { - // The test harness wants its own `main` and top-level functions, so - // never wrap the test in `fn main() { ... }`. - let (test, line_offset) = make_test(test, Some(cratename), as_test_harness, opts); + let (test, line_offset) = match panic::catch_unwind(|| { + make_test(test, Some(cratename), as_test_harness, opts) + }) { + Ok((test, line_offset)) => (test, line_offset), + Err(cause) if cause.is::() => { + // If the parser used by `make_test` panicked due to a fatal error, pass the test code + // through unchanged. The error will be reported during compilation. + (test.to_owned(), 0) + }, + Err(cause) => panic::resume_unwind(cause), + }; + // FIXME(#44940): if doctests ever support path remapping, then this filename // needs to be the result of `SourceMap::span_to_unmapped_path`. let path = match filename { @@ -337,7 +346,13 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize, } } -/// Makes the test file. Also returns the number of lines before the code begins +/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of +/// lines before the test code begins. +/// +/// # Panics +/// +/// This function uses the compiler's parser internally. The parser will panic if it encounters a +/// fatal error while parsing the test. pub fn make_test(s: &str, cratename: Option<&str>, dont_insert_main: bool, diff --git a/src/libstd/sys/redox/ext/net.rs b/src/libstd/sys/redox/ext/net.rs index 096d0681959cd..b3ef5f3064c16 100644 --- a/src/libstd/sys/redox/ext/net.rs +++ b/src/libstd/sys/redox/ext/net.rs @@ -1,4 +1,4 @@ -#![stable(feature = "unix_socket_redox", since = "1.29")] +#![stable(feature = "unix_socket_redox", since = "1.29.0")] //! Unix-specific networking functionality @@ -27,7 +27,7 @@ use crate::sys::{cvt, fd::FileDesc, syscall}; /// let addr = socket.local_addr().expect("Couldn't get local address"); /// ``` #[derive(Clone)] -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] pub struct SocketAddr(()); impl SocketAddr { @@ -55,7 +55,7 @@ impl SocketAddr { /// let addr = socket.local_addr().expect("Couldn't get local address"); /// assert_eq!(addr.as_pathname(), None); /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn as_pathname(&self) -> Option<&Path> { None } @@ -83,12 +83,12 @@ impl SocketAddr { /// let addr = socket.local_addr().expect("Couldn't get local address"); /// assert_eq!(addr.is_unnamed(), true); /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn is_unnamed(&self) -> bool { false } } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl fmt::Debug for SocketAddr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "SocketAddr") @@ -109,10 +109,10 @@ impl fmt::Debug for SocketAddr { /// stream.read_to_string(&mut response).unwrap(); /// println!("{}", response); /// ``` -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] pub struct UnixStream(FileDesc); -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl fmt::Debug for UnixStream { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut builder = fmt.debug_struct("UnixStream"); @@ -143,7 +143,7 @@ impl UnixStream { /// } /// }; /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn connect>(path: P) -> io::Result { if let Some(s) = path.as_ref().to_str() { cvt(syscall::open(format!("chan:{}", s), syscall::O_CLOEXEC)) @@ -174,7 +174,7 @@ impl UnixStream { /// } /// }; /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn pair() -> io::Result<(UnixStream, UnixStream)> { let server = cvt(syscall::open("chan:", syscall::O_CREAT | syscall::O_CLOEXEC)) .map(FileDesc::new)?; @@ -198,7 +198,7 @@ impl UnixStream { /// let socket = UnixStream::connect("/tmp/sock").unwrap(); /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn try_clone(&self) -> io::Result { self.0.duplicate().map(UnixStream) } @@ -213,7 +213,7 @@ impl UnixStream { /// let socket = UnixStream::connect("/tmp/sock").unwrap(); /// let addr = socket.local_addr().expect("Couldn't get local address"); /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn local_addr(&self) -> io::Result { Err(Error::new(ErrorKind::Other, "UnixStream::local_addr unimplemented on redox")) } @@ -228,7 +228,7 @@ impl UnixStream { /// let socket = UnixStream::connect("/tmp/sock").unwrap(); /// let addr = socket.peer_addr().expect("Couldn't get peer address"); /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn peer_addr(&self) -> io::Result { Err(Error::new(ErrorKind::Other, "UnixStream::peer_addr unimplemented on redox")) } @@ -267,7 +267,7 @@ impl UnixStream { /// let err = result.unwrap_err(); /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn set_read_timeout(&self, _timeout: Option) -> io::Result<()> { Err(Error::new(ErrorKind::Other, "UnixStream::set_read_timeout unimplemented on redox")) } @@ -306,7 +306,7 @@ impl UnixStream { /// let err = result.unwrap_err(); /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn set_write_timeout(&self, _timeout: Option) -> io::Result<()> { Err(Error::new(ErrorKind::Other, "UnixStream::set_write_timeout unimplemented on redox")) } @@ -323,7 +323,7 @@ impl UnixStream { /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); /// assert_eq!(socket.read_timeout().unwrap(), Some(Duration::new(1, 0))); /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn read_timeout(&self) -> io::Result> { Err(Error::new(ErrorKind::Other, "UnixStream::read_timeout unimplemented on redox")) } @@ -340,7 +340,7 @@ impl UnixStream { /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout"); /// assert_eq!(socket.write_timeout().unwrap(), Some(Duration::new(1, 0))); /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn write_timeout(&self) -> io::Result> { Err(Error::new(ErrorKind::Other, "UnixStream::write_timeout unimplemented on redox")) } @@ -355,7 +355,7 @@ impl UnixStream { /// let socket = UnixStream::connect("/tmp/sock").unwrap(); /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } @@ -375,7 +375,7 @@ impl UnixStream { /// /// # Platform specific /// On Redox this always returns `None`. - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn take_error(&self) -> io::Result> { Ok(None) } @@ -397,13 +397,13 @@ impl UnixStream { /// let socket = UnixStream::connect("/tmp/sock").unwrap(); /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn shutdown(&self, _how: Shutdown) -> io::Result<()> { Err(Error::new(ErrorKind::Other, "UnixStream::shutdown unimplemented on redox")) } } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl io::Read for UnixStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { io::Read::read(&mut &*self, buf) @@ -415,7 +415,7 @@ impl io::Read for UnixStream { } } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl<'a> io::Read for &'a UnixStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) @@ -427,7 +427,7 @@ impl<'a> io::Read for &'a UnixStream { } } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl io::Write for UnixStream { fn write(&mut self, buf: &[u8]) -> io::Result { io::Write::write(&mut &*self, buf) @@ -438,7 +438,7 @@ impl io::Write for UnixStream { } } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl<'a> io::Write for &'a UnixStream { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) @@ -449,21 +449,21 @@ impl<'a> io::Write for &'a UnixStream { } } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl AsRawFd for UnixStream { fn as_raw_fd(&self) -> RawFd { self.0.raw() } } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl FromRawFd for UnixStream { unsafe fn from_raw_fd(fd: RawFd) -> UnixStream { UnixStream(FileDesc::new(fd)) } } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl IntoRawFd for UnixStream { fn into_raw_fd(self) -> RawFd { self.0.into_raw() @@ -498,10 +498,10 @@ impl IntoRawFd for UnixStream { /// } /// } /// ``` -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] pub struct UnixListener(FileDesc); -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl fmt::Debug for UnixListener { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut builder = fmt.debug_struct("UnixListener"); @@ -529,7 +529,7 @@ impl UnixListener { /// } /// }; /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn bind>(path: P) -> io::Result { if let Some(s) = path.as_ref().to_str() { cvt(syscall::open(format!("chan:{}", s), syscall::O_CREAT | syscall::O_CLOEXEC)) @@ -563,7 +563,7 @@ impl UnixListener { /// Err(e) => println!("accept function failed: {:?}", e), /// } /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { self.0.duplicate_path(b"listen").map(|fd| (UnixStream(fd), SocketAddr(()))) } @@ -583,7 +583,7 @@ impl UnixListener { /// /// let listener_copy = listener.try_clone().expect("try_clone failed"); /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn try_clone(&self) -> io::Result { self.0.duplicate().map(UnixListener) } @@ -599,7 +599,7 @@ impl UnixListener { /// /// let addr = listener.local_addr().expect("Couldn't get local address"); /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn local_addr(&self) -> io::Result { Err(Error::new(ErrorKind::Other, "UnixListener::local_addr unimplemented on redox")) } @@ -615,7 +615,7 @@ impl UnixListener { /// /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } @@ -636,7 +636,7 @@ impl UnixListener { /// /// # Platform specific /// On Redox this always returns `None`. - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn take_error(&self) -> io::Result> { Ok(None) } @@ -672,34 +672,34 @@ impl UnixListener { /// } /// } /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] + #[stable(feature = "unix_socket_redox", since = "1.29.0")] pub fn incoming<'a>(&'a self) -> Incoming<'a> { Incoming { listener: self } } } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl AsRawFd for UnixListener { fn as_raw_fd(&self) -> RawFd { self.0.raw() } } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl FromRawFd for UnixListener { unsafe fn from_raw_fd(fd: RawFd) -> UnixListener { UnixListener(FileDesc::new(fd)) } } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl IntoRawFd for UnixListener { fn into_raw_fd(self) -> RawFd { self.0.into_raw() } } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl<'a> IntoIterator for &'a UnixListener { type Item = io::Result; type IntoIter = Incoming<'a>; @@ -740,12 +740,12 @@ impl<'a> IntoIterator for &'a UnixListener { /// } /// ``` #[derive(Debug)] -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] pub struct Incoming<'a> { listener: &'a UnixListener, } -#[stable(feature = "unix_socket_redox", since = "1.29")] +#[stable(feature = "unix_socket_redox", since = "1.29.0")] impl<'a> Iterator for Incoming<'a> { type Item = io::Result; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 012fcbdd8c8e2..2a1f3c4801406 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -109,15 +109,14 @@ macro_rules! declare_features { // stable (active). // // Note that the features should be grouped into internal/user-facing -// and then sorted by version inside those groups. -// FIXME(60361): Enforce ^-- with tidy. +// and then sorted by version inside those groups. This is inforced with tidy. // // N.B., `tools/tidy/src/features.rs` parses this information directly out of the // source, so take care when modifying it. declare_features! ( // ------------------------------------------------------------------------- - // Internal feature gates. + // feature-group-start: internal feature gates // ------------------------------------------------------------------------- // no tracking issue START @@ -211,12 +210,12 @@ declare_features! ( // no tracking issue END - // Allows using the `may_dangle` attribute (RFC 1327). - (active, dropck_eyepatch, "1.10.0", Some(34761), None), - // Allows using `#[structural_match]` which indicates that a type is structurally matchable. (active, structural_match, "1.8.0", Some(31434), None), + // Allows using the `may_dangle` attribute (RFC 1327). + (active, dropck_eyepatch, "1.10.0", Some(34761), None), + // Allows using the `#![panic_runtime]` attribute. (active, panic_runtime, "1.10.0", Some(32837), None), @@ -252,7 +251,11 @@ declare_features! ( (active, test_2018_feature, "1.31.0", Some(0), Some(Edition::Edition2018)), // ------------------------------------------------------------------------- - // Actual feature gates (target features). + // feature-group-end: internal feature gates + // ------------------------------------------------------------------------- + + // ------------------------------------------------------------------------- + // feature-group-start: actual feature gates (target features) // ------------------------------------------------------------------------- // FIXME: Document these and merge with the list below. @@ -275,7 +278,11 @@ declare_features! ( (active, f16c_target_feature, "1.36.0", Some(44839), None), // ------------------------------------------------------------------------- - // Actual feature gates. + // feature-group-end: actual feature gates (target features) + // ------------------------------------------------------------------------- + + // ------------------------------------------------------------------------- + // feature-group-start: actual feature gates // ------------------------------------------------------------------------- // Allows using `asm!` macro with which inline assembly can be embedded. @@ -340,9 +347,6 @@ declare_features! ( // Permits specifying whether a function should permit unwinding or abort on unwind. (active, unwind_attributes, "1.4.0", Some(58760), None), - // Allows using `#[naked]` on functions. - (active, naked_functions, "1.9.0", Some(32408), None), - // Allows `#[no_debug]`. (active, no_debug, "1.5.0", Some(29721), None), @@ -358,6 +362,9 @@ declare_features! ( // Allows specialization of implementations (RFC 1210). (active, specialization, "1.7.0", Some(31844), None), + // Allows using `#[naked]` on functions. + (active, naked_functions, "1.9.0", Some(32408), None), + // Allows `cfg(target_has_atomic = "...")`. (active, cfg_target_has_atomic, "1.9.0", Some(32976), None), @@ -545,6 +552,10 @@ declare_features! ( // Allows using C-variadics. (active, c_variadic, "1.34.0", Some(44930), None), + + // ------------------------------------------------------------------------- + // feature-group-end: actual feature gates + // ------------------------------------------------------------------------- ); // Some features are known to be incomplete and using them is likely to have diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f70acb3e7da9a..c5173aa556953 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -8725,9 +8725,9 @@ impl<'a> Parser<'a> { // Check if this is a ident pattern, if so, we can optimize and avoid adding a // `let = __argN;` statement, instead just adding a `let = ;` // statement. - let (ident, is_simple_pattern) = match input.pat.node { - PatKind::Ident(_, ident, _) => (ident, true), - _ => (ident, false), + let (binding_mode, ident, is_simple_pattern) = match input.pat.node { + PatKind::Ident(binding_mode, ident, _) => (binding_mode, ident, true), + _ => (BindingMode::ByValue(Mutability::Immutable), ident, false), }; // Construct an argument representing `__argN: ` to replace the argument of the @@ -8755,9 +8755,7 @@ impl<'a> Parser<'a> { let move_local = Local { pat: P(Pat { id, - node: PatKind::Ident( - BindingMode::ByValue(Mutability::Immutable), ident, None, - ), + node: PatKind::Ident(binding_mode, ident, None), span, }), // We explicitly do not specify the type for this statement. When the user's diff --git a/src/test/run-make-fulldeps/libs-through-symlinks/Makefile b/src/test/run-make-fulldeps/libs-through-symlinks/Makefile index 7c5ea7e402e56..8be2e234fea1f 100644 --- a/src/test/run-make-fulldeps/libs-through-symlinks/Makefile +++ b/src/test/run-make-fulldeps/libs-through-symlinks/Makefile @@ -8,4 +8,4 @@ all: mkdir -p $(TMPDIR)/outdir $(RUSTC) foo.rs -o $(TMPDIR)/outdir/$(NAME) ln -nsf outdir/$(NAME) $(TMPDIR) - RUST_LOG=rustc_metadata::loader $(RUSTC) bar.rs + RUSTC_LOG=rustc_metadata::loader $(RUSTC) bar.rs diff --git a/src/test/run-pass/existential_type.rs b/src/test/run-pass/existential_type.rs index dfb195ec83062..b36435cf113f1 100644 --- a/src/test/run-pass/existential_type.rs +++ b/src/test/run-pass/existential_type.rs @@ -68,14 +68,14 @@ fn my_other_iter(u: U) -> MyOtherIter { } trait Trait {} -existential type GenericBound<'a, T: Trait>: 'a; +existential type GenericBound<'a, T: Trait>: Sized + 'a; fn generic_bound<'a, T: Trait + 'a>(t: T) -> GenericBound<'a, T> { t } mod pass_through { - pub existential type Passthrough: 'static; + pub existential type Passthrough: Sized + 'static; fn define_passthrough(t: T) -> Passthrough { t diff --git a/src/test/run-pass/issues/issue-18075.rs b/src/test/run-pass/issues/issue-18075.rs index dcd67d8d15ab6..ee6845c1278b8 100644 --- a/src/test/run-pass/issues/issue-18075.rs +++ b/src/test/run-pass/issues/issue-18075.rs @@ -1,5 +1,5 @@ // run-pass -// exec-env:RUST_LOG=rustc::middle=debug +// exec-env:RUSTC_LOG=rustc::middle=debug fn main() { let b = 1isize; diff --git a/src/test/run-pass/logging-only-prints-once.rs b/src/test/run-pass/logging-only-prints-once.rs index 7b10560022565..1a4c4d89e7dc8 100644 --- a/src/test/run-pass/logging-only-prints-once.rs +++ b/src/test/run-pass/logging-only-prints-once.rs @@ -1,6 +1,6 @@ // ignore-windows // ignore-emscripten no threads support -// exec-env:RUST_LOG=debug +// exec-env:RUSTC_LOG=debug use std::cell::Cell; use std::fmt; diff --git a/src/test/run-pass/logging_before_rt_started.rs b/src/test/run-pass/logging_before_rt_started.rs index 0cba1080de265..69cfc54c465b5 100644 --- a/src/test/run-pass/logging_before_rt_started.rs +++ b/src/test/run-pass/logging_before_rt_started.rs @@ -1,4 +1,4 @@ -// exec-env:RUST_LOG=std::ptr +// exec-env:RUSTC_LOG=std::ptr // In issue #9487, it was realized that std::ptr was invoking the logging // infrastructure, and when std::ptr was used during runtime initialization, diff --git a/src/test/run-pass/rustc-rust-log.rs b/src/test/run-pass/rustc-rust-log.rs index 4360a15401534..b664257241833 100644 --- a/src/test/run-pass/rustc-rust-log.rs +++ b/src/test/run-pass/rustc-rust-log.rs @@ -8,6 +8,6 @@ // dont-check-compiler-stderr // compile-flags: --error-format human -// rustc-env:RUST_LOG=debug +// rustc-env:RUSTC_LOG=debug fn main() {} diff --git a/src/test/run-pass/threads-sendsync/spawning-with-debug.rs b/src/test/run-pass/threads-sendsync/spawning-with-debug.rs index c78ae78dd1ba2..388d62aa7101c 100644 --- a/src/test/run-pass/threads-sendsync/spawning-with-debug.rs +++ b/src/test/run-pass/threads-sendsync/spawning-with-debug.rs @@ -2,7 +2,7 @@ #![allow(unused_must_use)] #![allow(unused_mut)] // ignore-windows -// exec-env:RUST_LOG=debug +// exec-env:RUSTC_LOG=debug // ignore-emscripten no threads support // regression test for issue #10405, make sure we don't call println! too soon. diff --git a/src/test/rustdoc-ui/failed-doctest-output.stdout b/src/test/rustdoc-ui/failed-doctest-output.stdout index c9f59405ce012..7b1cd70273140 100644 --- a/src/test/rustdoc-ui/failed-doctest-output.stdout +++ b/src/test/rustdoc-ui/failed-doctest-output.stdout @@ -15,7 +15,7 @@ error[E0425]: cannot find value `no` in this scope error: aborting due to previous error For more information about this error, try `rustc --explain E0425`. -thread '$DIR/failed-doctest-output.rs - OtherStruct (line 17)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:310:13 +thread '$DIR/failed-doctest-output.rs - OtherStruct (line 17)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:319:13 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace. ---- $DIR/failed-doctest-output.rs - SomeStruct (line 11) stdout ---- @@ -24,7 +24,7 @@ thread '$DIR/failed-doctest-output.rs - SomeStruct (line 11)' panicked at 'test thread 'main' panicked at 'oh no', $DIR/failed-doctest-output.rs:3:1 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace. -', src/librustdoc/test.rs:332:17 +', src/librustdoc/test.rs:341:17 failures: diff --git a/src/test/rustdoc-ui/unparseable-doc-test.rs b/src/test/rustdoc-ui/unparseable-doc-test.rs new file mode 100644 index 0000000000000..18d6b32bf4037 --- /dev/null +++ b/src/test/rustdoc-ui/unparseable-doc-test.rs @@ -0,0 +1,10 @@ +// compile-flags: --test +// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" +// failure-status: 101 +// rustc-env: RUST_BACKTRACE=0 + +/// ```rust +/// let x = 7; +/// "unterminated +/// ``` +pub fn foo() {} diff --git a/src/test/rustdoc-ui/unparseable-doc-test.stdout b/src/test/rustdoc-ui/unparseable-doc-test.stdout new file mode 100644 index 0000000000000..7048ef2c58977 --- /dev/null +++ b/src/test/rustdoc-ui/unparseable-doc-test.stdout @@ -0,0 +1,24 @@ + +running 1 test +test $DIR/unparseable-doc-test.rs - foo (line 6) ... FAILED + +failures: + +---- $DIR/unparseable-doc-test.rs - foo (line 6) stdout ---- +error: unterminated double quote string + --> $DIR/unparseable-doc-test.rs:8:1 + | +2 | "unterminated + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + +thread '$DIR/unparseable-doc-test.rs - foo (line 6)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:319:13 +note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace. + + +failures: + $DIR/unparseable-doc-test.rs - foo (line 6) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out + diff --git a/src/test/ui/async-await/mutable-arguments.rs b/src/test/ui/async-await/mutable-arguments.rs new file mode 100644 index 0000000000000..4d6dba74097ca --- /dev/null +++ b/src/test/ui/async-await/mutable-arguments.rs @@ -0,0 +1,10 @@ +// edition:2018 +// run-pass + +#![feature(async_await)] + +async fn foo(n: u32, mut vec: Vec) { + vec.push(n); +} + +fn main() {} diff --git a/src/test/ui/async-fn-path-elision.rs b/src/test/ui/async-fn-path-elision.rs new file mode 100644 index 0000000000000..8db7631ef4108 --- /dev/null +++ b/src/test/ui/async-fn-path-elision.rs @@ -0,0 +1,16 @@ +// edition:2018 + +#![feature(async_await, await_macro)] +#![allow(dead_code)] + +struct HasLifetime<'a>(&'a bool); + +async fn error(lt: HasLifetime) { //~ ERROR implicit elided lifetime not allowed here + if *lt.0 {} +} + +fn no_error(lt: HasLifetime) { + if *lt.0 {} +} + +fn main() {} diff --git a/src/test/ui/async-fn-path-elision.stderr b/src/test/ui/async-fn-path-elision.stderr new file mode 100644 index 0000000000000..3b311baba01de --- /dev/null +++ b/src/test/ui/async-fn-path-elision.stderr @@ -0,0 +1,8 @@ +error[E0726]: implicit elided lifetime not allowed here + --> $DIR/async-fn-path-elision.rs:8:20 + | +LL | async fn error(lt: HasLifetime) { + | ^^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>` + +error: aborting due to previous error + diff --git a/src/test/ui/consts/const-err.rs b/src/test/ui/consts/const-err.rs index 8cc3dc7f58744..7dfcda69058ac 100644 --- a/src/test/ui/consts/const-err.rs +++ b/src/test/ui/consts/const-err.rs @@ -13,4 +13,5 @@ const FOO: u8 = [5u8][1]; fn main() { black_box((FOO, FOO)); //~^ ERROR erroneous constant used + //~| ERROR erroneous constant } diff --git a/src/test/ui/consts/const-err.stderr b/src/test/ui/consts/const-err.stderr index 0ee9ecdef451d..429e2ae7600d6 100644 --- a/src/test/ui/consts/const-err.stderr +++ b/src/test/ui/consts/const-err.stderr @@ -13,11 +13,17 @@ LL | #![warn(const_err)] | ^^^^^^^^^ error[E0080]: erroneous constant used - --> $DIR/const-err.rs:14:15 + --> $DIR/const-err.rs:14:16 | LL | black_box((FOO, FOO)); - | ^^^^^^^^^^ referenced constant has errors + | ^^^ referenced constant has errors -error: aborting due to previous error +error[E0080]: erroneous constant used + --> $DIR/const-err.rs:14:21 + | +LL | black_box((FOO, FOO)); + | ^^^ referenced constant has errors + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/destructure-trait-ref.stderr b/src/test/ui/destructure-trait-ref.stderr index 34dd213e2b390..bc3013b78b38c 100644 --- a/src/test/ui/destructure-trait-ref.stderr +++ b/src/test/ui/destructure-trait-ref.stderr @@ -20,21 +20,25 @@ error[E0308]: mismatched types --> $DIR/destructure-trait-ref.rs:31:10 | LL | let &&x = &1isize as &T; - | ^^ expected trait T, found reference + | ^^ + | | + | expected trait T, found reference + | help: you can probably remove the explicit borrow: `x` | = note: expected type `dyn T` found type `&_` - = help: did you mean `x: &dyn T`? error[E0308]: mismatched types --> $DIR/destructure-trait-ref.rs:36:11 | LL | let &&&x = &(&1isize as &T); - | ^^ expected trait T, found reference + | ^^ + | | + | expected trait T, found reference + | help: you can probably remove the explicit borrow: `x` | = note: expected type `dyn T` found type `&_` - = help: did you mean `x: &dyn T`? error[E0308]: mismatched types --> $DIR/destructure-trait-ref.rs:41:13 diff --git a/src/test/ui/existential_types/existential-types-with-no-traits.rs b/src/test/ui/existential_types/existential-types-with-no-traits.rs new file mode 100644 index 0000000000000..46339c73b1f1d --- /dev/null +++ b/src/test/ui/existential_types/existential-types-with-no-traits.rs @@ -0,0 +1,14 @@ +#![feature(existential_type)] + +existential type Foo: 'static; +//~^ ERROR: at least one trait must be specified + +fn foo() -> Foo { + "foo" +} + +fn bar() -> impl 'static { //~ ERROR: at least one trait must be specified + "foo" +} + +fn main() {} diff --git a/src/test/ui/existential_types/existential-types-with-no-traits.stderr b/src/test/ui/existential_types/existential-types-with-no-traits.stderr new file mode 100644 index 0000000000000..4b2fbc79d3bc2 --- /dev/null +++ b/src/test/ui/existential_types/existential-types-with-no-traits.stderr @@ -0,0 +1,14 @@ +error: at least one trait must be specified + --> $DIR/existential-types-with-no-traits.rs:3:23 + | +LL | existential type Foo: 'static; + | ^^^^^^^ + +error: at least one trait must be specified + --> $DIR/existential-types-with-no-traits.rs:10:13 + | +LL | fn bar() -> impl 'static { + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/existential_types/generic_nondefining_use.rs b/src/test/ui/existential_types/generic_nondefining_use.rs index 75af5d9570ff2..ffc965aca47c9 100644 --- a/src/test/ui/existential_types/generic_nondefining_use.rs +++ b/src/test/ui/existential_types/generic_nondefining_use.rs @@ -4,6 +4,8 @@ fn main() {} existential type Cmp: 'static; //~^ ERROR could not find defining uses +//~^^ ERROR: at least one trait must be specified + // not a defining use, because it doesn't define *all* possible generics fn cmp() -> Cmp { //~ ERROR defining existential type use does not fully define diff --git a/src/test/ui/existential_types/generic_nondefining_use.stderr b/src/test/ui/existential_types/generic_nondefining_use.stderr index ef579260f061c..d205d44c68c71 100644 --- a/src/test/ui/existential_types/generic_nondefining_use.stderr +++ b/src/test/ui/existential_types/generic_nondefining_use.stderr @@ -1,5 +1,11 @@ +error: at least one trait must be specified + --> $DIR/generic_nondefining_use.rs:5:26 + | +LL | existential type Cmp: 'static; + | ^^^^^^^ + error: defining existential type use does not fully define existential type - --> $DIR/generic_nondefining_use.rs:9:1 + --> $DIR/generic_nondefining_use.rs:11:1 | LL | / fn cmp() -> Cmp { LL | | 5u32 @@ -12,5 +18,5 @@ error: could not find defining uses LL | existential type Cmp: 'static; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/src/test/ui/existential_types/generic_not_used.rs b/src/test/ui/existential_types/generic_not_used.rs index bfe7b8c4a1d53..054e6f5f2ade9 100644 --- a/src/test/ui/existential_types/generic_not_used.rs +++ b/src/test/ui/existential_types/generic_not_used.rs @@ -3,6 +3,7 @@ fn main() {} existential type WrongGeneric: 'static; +//~^ ERROR: at least one trait must be specified fn wrong_generic(_: U, v: V) -> WrongGeneric { //~^ ERROR type parameter `V` is part of concrete type but not used in parameter list diff --git a/src/test/ui/existential_types/generic_not_used.stderr b/src/test/ui/existential_types/generic_not_used.stderr index 1ae4ab65929f0..d243233992b02 100644 --- a/src/test/ui/existential_types/generic_not_used.stderr +++ b/src/test/ui/existential_types/generic_not_used.stderr @@ -1,5 +1,11 @@ +error: at least one trait must be specified + --> $DIR/generic_not_used.rs:5:44 + | +LL | existential type WrongGeneric: 'static; + | ^^^^^^^ + error: type parameter `V` is part of concrete type but not used in parameter list for existential type - --> $DIR/generic_not_used.rs:7:73 + --> $DIR/generic_not_used.rs:8:73 | LL | fn wrong_generic(_: U, v: V) -> WrongGeneric { | _________________________________________________________________________^ @@ -8,5 +14,5 @@ LL | | v LL | | } | |_^ -error: aborting due to previous error +error: aborting due to 2 previous errors diff --git a/src/test/ui/existential_types/generic_type_does_not_live_long_enough.rs b/src/test/ui/existential_types/generic_type_does_not_live_long_enough.rs index 02bb151ccb618..d9eedd6dca7c1 100644 --- a/src/test/ui/existential_types/generic_type_does_not_live_long_enough.rs +++ b/src/test/ui/existential_types/generic_type_does_not_live_long_enough.rs @@ -8,6 +8,7 @@ fn main() { existential type WrongGeneric: 'static; //~^ ERROR the parameter type `T` may not live long enough +//~^^ ERROR: at least one trait must be specified fn wrong_generic(t: T) -> WrongGeneric { t diff --git a/src/test/ui/existential_types/generic_type_does_not_live_long_enough.stderr b/src/test/ui/existential_types/generic_type_does_not_live_long_enough.stderr index 002acc41553b6..2f76eea4460bd 100644 --- a/src/test/ui/existential_types/generic_type_does_not_live_long_enough.stderr +++ b/src/test/ui/existential_types/generic_type_does_not_live_long_enough.stderr @@ -1,3 +1,9 @@ +error: at least one trait must be specified + --> $DIR/generic_type_does_not_live_long_enough.rs:9:35 + | +LL | existential type WrongGeneric: 'static; + | ^^^^^^^ + error[E0308]: mismatched types --> $DIR/generic_type_does_not_live_long_enough.rs:6:18 | @@ -22,7 +28,7 @@ note: ...so that the type `T` will meet its required lifetime bounds LL | existential type WrongGeneric: 'static; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0308, E0310. For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/existential_types/generic_underconstrained.rs b/src/test/ui/existential_types/generic_underconstrained.rs index 967faca067c1e..cc0db893c6aa7 100644 --- a/src/test/ui/existential_types/generic_underconstrained.rs +++ b/src/test/ui/existential_types/generic_underconstrained.rs @@ -4,6 +4,7 @@ fn main() {} trait Trait {} existential type Underconstrained: 'static; //~ ERROR the trait bound `T: Trait` +//~^ ERROR: at least one trait must be specified // no `Trait` bound fn underconstrain(_: T) -> Underconstrained { diff --git a/src/test/ui/existential_types/generic_underconstrained.stderr b/src/test/ui/existential_types/generic_underconstrained.stderr index 8551a939e8ed8..35083a53eb343 100644 --- a/src/test/ui/existential_types/generic_underconstrained.stderr +++ b/src/test/ui/existential_types/generic_underconstrained.stderr @@ -1,3 +1,9 @@ +error: at least one trait must be specified + --> $DIR/generic_underconstrained.rs:6:46 + | +LL | existential type Underconstrained: 'static; + | ^^^^^^^ + error[E0277]: the trait bound `T: Trait` is not satisfied --> $DIR/generic_underconstrained.rs:6:1 | @@ -7,6 +13,6 @@ LL | existential type Underconstrained: 'static; = help: consider adding a `where T: Trait` bound = note: the return type of a function must have a statically known size -error: aborting due to previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/existential_types/generic_underconstrained2.rs b/src/test/ui/existential_types/generic_underconstrained2.rs index 98d9da832cfd3..c6263eacd53e3 100644 --- a/src/test/ui/existential_types/generic_underconstrained2.rs +++ b/src/test/ui/existential_types/generic_underconstrained2.rs @@ -4,6 +4,7 @@ fn main() {} existential type Underconstrained: 'static; //~^ ERROR `U` doesn't implement `std::fmt::Debug` +//~^^ ERROR: at least one trait must be specified // not a defining use, because it doesn't define *all* possible generics fn underconstrained(_: U) -> Underconstrained { @@ -12,6 +13,7 @@ fn underconstrained(_: U) -> Underconstrained { existential type Underconstrained2: 'static; //~^ ERROR `V` doesn't implement `std::fmt::Debug` +//~^^ ERROR: at least one trait must be specified // not a defining use, because it doesn't define *all* possible generics fn underconstrained2(_: U, _: V) -> Underconstrained2 { diff --git a/src/test/ui/existential_types/generic_underconstrained2.stderr b/src/test/ui/existential_types/generic_underconstrained2.stderr index c7b6d6ade557c..6ff783f33b96f 100644 --- a/src/test/ui/existential_types/generic_underconstrained2.stderr +++ b/src/test/ui/existential_types/generic_underconstrained2.stderr @@ -1,3 +1,15 @@ +error: at least one trait must be specified + --> $DIR/generic_underconstrained2.rs:5:56 + | +LL | existential type Underconstrained: 'static; + | ^^^^^^^ + +error: at least one trait must be specified + --> $DIR/generic_underconstrained2.rs:14:57 + | +LL | existential type Underconstrained2: 'static; + | ^^^^^^^ + error[E0277]: `U` doesn't implement `std::fmt::Debug` --> $DIR/generic_underconstrained2.rs:5:1 | @@ -9,7 +21,7 @@ LL | existential type Underconstrained: 'static; = note: the return type of a function must have a statically known size error[E0277]: `V` doesn't implement `std::fmt::Debug` - --> $DIR/generic_underconstrained2.rs:13:1 + --> $DIR/generic_underconstrained2.rs:14:1 | LL | existential type Underconstrained2: 'static; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` @@ -18,6 +30,6 @@ LL | existential type Underconstrained2: 'static; = help: consider adding a `where V: std::fmt::Debug` bound = note: the return type of a function must have a statically known size -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/existential_types/unused_generic_param.rs b/src/test/ui/existential_types/unused_generic_param.rs index 7af6508788129..5455b39f4cbe2 100644 --- a/src/test/ui/existential_types/unused_generic_param.rs +++ b/src/test/ui/existential_types/unused_generic_param.rs @@ -1,18 +1,17 @@ -// compile-pass #![feature(existential_type)] fn main() { } -// test that unused generic parameters are ok existential type PartiallyDefined: 'static; +//~^ ERROR: at least one trait must be specified fn partially_defined(_: T) -> PartiallyDefined { 4u32 } -// test that unused generic parameters are ok existential type PartiallyDefined2: 'static; +//~^ ERROR: at least one trait must be specified fn partially_defined2(_: T) -> PartiallyDefined2 { 4u32 diff --git a/src/test/ui/existential_types/unused_generic_param.stderr b/src/test/ui/existential_types/unused_generic_param.stderr new file mode 100644 index 0000000000000..9d628d069d36c --- /dev/null +++ b/src/test/ui/existential_types/unused_generic_param.stderr @@ -0,0 +1,14 @@ +error: at least one trait must be specified + --> $DIR/unused_generic_param.rs:6:39 + | +LL | existential type PartiallyDefined: 'static; + | ^^^^^^^ + +error: at least one trait must be specified + --> $DIR/unused_generic_param.rs:13:40 + | +LL | existential type PartiallyDefined2: 'static; + | ^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/impl-header-lifetime-elision/path-elided.rs b/src/test/ui/impl-header-lifetime-elision/path-elided.rs index 6532b0aebe74a..40a52efc7f9fc 100644 --- a/src/test/ui/impl-header-lifetime-elision/path-elided.rs +++ b/src/test/ui/impl-header-lifetime-elision/path-elided.rs @@ -5,7 +5,7 @@ trait MyTrait { } struct Foo<'a> { x: &'a u32 } impl MyTrait for Foo { - //~^ ERROR missing lifetime specifier + //~^ ERROR implicit elided lifetime not allowed here } fn main() {} diff --git a/src/test/ui/impl-header-lifetime-elision/path-elided.stderr b/src/test/ui/impl-header-lifetime-elision/path-elided.stderr index c9287ffba96d3..6500a2a55f667 100644 --- a/src/test/ui/impl-header-lifetime-elision/path-elided.stderr +++ b/src/test/ui/impl-header-lifetime-elision/path-elided.stderr @@ -1,9 +1,8 @@ -error[E0106]: missing lifetime specifier +error[E0726]: implicit elided lifetime not allowed here --> $DIR/path-elided.rs:7:18 | LL | impl MyTrait for Foo { - | ^^^ expected lifetime parameter + | ^^^- help: indicate the anonymous lifetime: `<'_>` error: aborting due to previous error -For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/impl-header-lifetime-elision/trait-elided.rs b/src/test/ui/impl-header-lifetime-elision/trait-elided.rs index 3979eda740a82..102d259b0c87a 100644 --- a/src/test/ui/impl-header-lifetime-elision/trait-elided.rs +++ b/src/test/ui/impl-header-lifetime-elision/trait-elided.rs @@ -3,7 +3,7 @@ trait MyTrait<'a> { } impl MyTrait for u32 { - //~^ ERROR missing lifetime specifier + //~^ ERROR implicit elided lifetime not allowed here } fn main() {} diff --git a/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr b/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr index b742db1c75c19..ad97cb0abd623 100644 --- a/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr +++ b/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr @@ -1,9 +1,8 @@ -error[E0106]: missing lifetime specifier +error[E0726]: implicit elided lifetime not allowed here --> $DIR/trait-elided.rs:5:6 | LL | impl MyTrait for u32 { - | ^^^^^^^ expected lifetime parameter + | ^^^^^^^- help: indicate the anonymous lifetime: `<'_>` error: aborting due to previous error -For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/issues/issue-10412.rs b/src/test/ui/issues/issue-10412.rs index a0bc2fc2f3c79..020585136856b 100644 --- a/src/test/ui/issues/issue-10412.rs +++ b/src/test/ui/issues/issue-10412.rs @@ -5,7 +5,8 @@ trait Serializable<'self, T> { //~ ERROR lifetimes cannot use keyword names impl<'self> Serializable for &'self str { //~ ERROR lifetimes cannot use keyword names //~^ ERROR lifetimes cannot use keyword names - //~| ERROR missing lifetime specifier + //~| ERROR implicit elided lifetime not allowed here + //~| ERROR the size for values of type `str` cannot be known at compilation time fn serialize(val : &'self str) -> Vec { //~ ERROR lifetimes cannot use keyword names vec![1] } diff --git a/src/test/ui/issues/issue-10412.stderr b/src/test/ui/issues/issue-10412.stderr index 48920813ae10d..0793dd99b4d12 100644 --- a/src/test/ui/issues/issue-10412.stderr +++ b/src/test/ui/issues/issue-10412.stderr @@ -29,23 +29,32 @@ LL | impl<'self> Serializable for &'self str { | ^^^^^ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:9:25 + --> $DIR/issue-10412.rs:10:25 | LL | fn serialize(val : &'self str) -> Vec { | ^^^^^ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:12:37 + --> $DIR/issue-10412.rs:13:37 | LL | fn deserialize(repr: &[u8]) -> &'self str { | ^^^^^ -error[E0106]: missing lifetime specifier +error[E0726]: implicit elided lifetime not allowed here --> $DIR/issue-10412.rs:6:13 | LL | impl<'self> Serializable for &'self str { - | ^^^^^^^^^^^^^^^^^ expected lifetime parameter + | ^^^^^^^^^^^^^^^^^ help: indicate the anonymous lifetime: `Serializable<'_, str>` -error: aborting due to 8 previous errors +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/issue-10412.rs:6:13 + | +LL | impl<'self> Serializable for &'self str { + | ^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `str` + = note: to learn more, visit + +error: aborting due to 9 previous errors -For more information about this error, try `rustc --explain E0106`. +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/match/match-type-err-first-arm.rs b/src/test/ui/match/match-type-err-first-arm.rs index b4b84ef8f1cec..8dfbf1019e9a4 100644 --- a/src/test/ui/match/match-type-err-first-arm.rs +++ b/src/test/ui/match/match-type-err-first-arm.rs @@ -3,8 +3,7 @@ fn main() { let _ = test_func2(1); } -fn test_func1(n: i32) -> i32 { - //~^ NOTE expected `i32` because of return type +fn test_func1(n: i32) -> i32 { //~ NOTE expected `i32` because of return type match n { 12 => 'b', //~^ ERROR mismatched types @@ -14,10 +13,8 @@ fn test_func1(n: i32) -> i32 { } fn test_func2(n: i32) -> i32 { - let x = match n { - //~^ NOTE `match` arms have incompatible types - 12 => 'b', - //~^ NOTE this is found to be of type `char` + let x = match n { //~ NOTE `match` arms have incompatible types + 12 => 'b', //~ NOTE this is found to be of type `char` _ => 42, //~^ ERROR match arms have incompatible types //~| NOTE expected char, found integer @@ -27,8 +24,7 @@ fn test_func2(n: i32) -> i32 { } fn test_func3(n: i32) -> i32 { - let x = match n { - //~^ NOTE `match` arms have incompatible types + let x = match n { //~ NOTE `match` arms have incompatible types 1 => 'b', 2 => 'b', 3 => 'b', @@ -43,3 +39,15 @@ fn test_func3(n: i32) -> i32 { }; x } + +fn test_func4() { + match Some(0u32) { //~ NOTE `match` arms have incompatible types + Some(x) => { + x //~ NOTE this is found to be of type `u32` + }, + None => {} + //~^ ERROR match arms have incompatible types + //~| NOTE expected u32, found () + //~| NOTE expected type `u32` + }; +} diff --git a/src/test/ui/match/match-type-err-first-arm.stderr b/src/test/ui/match/match-type-err-first-arm.stderr index a318e6cffb937..e0553fca683a5 100644 --- a/src/test/ui/match/match-type-err-first-arm.stderr +++ b/src/test/ui/match/match-type-err-first-arm.stderr @@ -1,24 +1,23 @@ error[E0308]: mismatched types - --> $DIR/match-type-err-first-arm.rs:9:15 + --> $DIR/match-type-err-first-arm.rs:8:15 | LL | fn test_func1(n: i32) -> i32 { | --- expected `i32` because of return type -... +LL | match n { LL | 12 => 'b', | ^^^ expected i32, found char error[E0308]: match arms have incompatible types - --> $DIR/match-type-err-first-arm.rs:21:14 + --> $DIR/match-type-err-first-arm.rs:18:14 | LL | let x = match n { | _____________- -LL | | LL | | 12 => 'b', | | --- this is found to be of type `char` -LL | | LL | | _ => 42, | | ^^ expected char, found integer -... | +LL | | +LL | | LL | | LL | | }; | |_____- `match` arms have incompatible types @@ -27,13 +26,13 @@ LL | | }; found type `{integer}` error[E0308]: match arms have incompatible types - --> $DIR/match-type-err-first-arm.rs:39:14 + --> $DIR/match-type-err-first-arm.rs:35:14 | LL | let x = match n { | _____________- -LL | | LL | | 1 => 'b', LL | | 2 => 'b', +LL | | 3 => 'b', ... | LL | | 6 => 'b', | | --- this and all prior arms are found to be of type `char` @@ -48,6 +47,24 @@ LL | | }; = note: expected type `char` found type `{integer}` -error: aborting due to 3 previous errors +error[E0308]: match arms have incompatible types + --> $DIR/match-type-err-first-arm.rs:48:17 + | +LL | / match Some(0u32) { +LL | | Some(x) => { +LL | | x + | | - this is found to be of type `u32` +LL | | }, +LL | | None => {} + | | ^^ expected u32, found () +... | +LL | | +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected type `u32` + found type `()` + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/mismatched_types/issue-38371.stderr b/src/test/ui/mismatched_types/issue-38371.stderr index 236f742db3f71..a9347926bda0a 100644 --- a/src/test/ui/mismatched_types/issue-38371.stderr +++ b/src/test/ui/mismatched_types/issue-38371.stderr @@ -12,11 +12,13 @@ error[E0308]: mismatched types --> $DIR/issue-38371.rs:18:9 | LL | fn agh(&&bar: &u32) { - | ^^^^ expected u32, found reference + | ^^^^ + | | + | expected u32, found reference + | help: you can probably remove the explicit borrow: `bar` | = note: expected type `u32` found type `&_` - = help: did you mean `bar: &u32`? error[E0308]: mismatched types --> $DIR/issue-38371.rs:21:8 diff --git a/src/test/ui/suggestions/match-ergonomics.rs b/src/test/ui/suggestions/match-ergonomics.rs new file mode 100644 index 0000000000000..c4fc01469bf65 --- /dev/null +++ b/src/test/ui/suggestions/match-ergonomics.rs @@ -0,0 +1,41 @@ +fn main() { + let x = vec![1i32]; + match &x[..] { + [&v] => {}, //~ ERROR mismatched types + _ => {}, + } + match x { + [&v] => {}, //~ ERROR expected an array or slice + _ => {}, + } + match &x[..] { + [v] => {}, + _ => {}, + } + match &x[..] { + &[v] => {}, + _ => {}, + } + match x { + [v] => {}, //~ ERROR expected an array or slice + _ => {}, + } + let y = 1i32; + match &y { + &v => {}, + _ => {}, + } + match y { + &v => {}, //~ ERROR mismatched types + _ => {}, + } + match &y { + v => {}, + _ => {}, + } + match y { + v => {}, + _ => {}, + } + if let [&v] = &x[..] {} //~ ERROR mismatched types +} diff --git a/src/test/ui/suggestions/match-ergonomics.stderr b/src/test/ui/suggestions/match-ergonomics.stderr new file mode 100644 index 0000000000000..b7497be6ceb36 --- /dev/null +++ b/src/test/ui/suggestions/match-ergonomics.stderr @@ -0,0 +1,52 @@ +error[E0308]: mismatched types + --> $DIR/match-ergonomics.rs:4:10 + | +LL | [&v] => {}, + | ^^ + | | + | expected i32, found reference + | help: you can probably remove the explicit borrow: `v` + | + = note: expected type `i32` + found type `&_` + +error[E0529]: expected an array or slice, found `std::vec::Vec` + --> $DIR/match-ergonomics.rs:8:9 + | +LL | [&v] => {}, + | ^^^^ pattern cannot match with input type `std::vec::Vec` + +error[E0529]: expected an array or slice, found `std::vec::Vec` + --> $DIR/match-ergonomics.rs:20:9 + | +LL | [v] => {}, + | ^^^ pattern cannot match with input type `std::vec::Vec` + +error[E0308]: mismatched types + --> $DIR/match-ergonomics.rs:29:9 + | +LL | &v => {}, + | ^^ + | | + | expected i32, found reference + | help: you can probably remove the explicit borrow: `v` + | + = note: expected type `i32` + found type `&_` + +error[E0308]: mismatched types + --> $DIR/match-ergonomics.rs:40:13 + | +LL | if let [&v] = &x[..] {} + | ^^ + | | + | expected i32, found reference + | help: you can probably remove the explicit borrow: `v` + | + = note: expected type `i32` + found type `&_` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0308, E0529. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-enum-variants-priority.rs b/src/test/ui/type-alias-enum-variants-priority.rs index db1da2b12e256..82cd21b09d3e7 100644 --- a/src/test/ui/type-alias-enum-variants-priority.rs +++ b/src/test/ui/type-alias-enum-variants-priority.rs @@ -1,5 +1,4 @@ #![feature(type_alias_enum_variants)] -#![deny(ambiguous_associated_items)] enum E { V diff --git a/src/test/ui/type-alias-enum-variants-priority.stderr b/src/test/ui/type-alias-enum-variants-priority.stderr index dcf7dc77ed5ea..b8271807b835d 100644 --- a/src/test/ui/type-alias-enum-variants-priority.stderr +++ b/src/test/ui/type-alias-enum-variants-priority.stderr @@ -1,23 +1,19 @@ error: ambiguous associated item - --> $DIR/type-alias-enum-variants-priority.rs:15:15 + --> $DIR/type-alias-enum-variants-priority.rs:14:15 | LL | fn f() -> Self::V { 0 } | ^^^^^^^ help: use fully-qualified syntax: `::V` | -note: lint level defined here - --> $DIR/type-alias-enum-variants-priority.rs:2:9 - | -LL | #![deny(ambiguous_associated_items)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: #[deny(ambiguous_associated_items)] on by default = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #57644 note: `V` could refer to variant defined here - --> $DIR/type-alias-enum-variants-priority.rs:5:5 + --> $DIR/type-alias-enum-variants-priority.rs:4:5 | LL | V | ^ note: `V` could also refer to associated type defined here - --> $DIR/type-alias-enum-variants-priority.rs:9:5 + --> $DIR/type-alias-enum-variants-priority.rs:8:5 | LL | type V; | ^^^^^^^ diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index f7b491823f838..f5db2487618d6 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Alex Crichton "] [dependencies] +regex = "1" serde = "1.0.8" serde_derive = "1.0.8" serde_json = "1.0.2" diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index 8239fd9dce0c1..3144df6dd4cdf 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -7,6 +7,7 @@ //! * Library features have at most one stability level. //! * Library features have at most one `since` value. //! * All unstable lang features have tests to ensure they are actually unstable. +//! * Language features in a group are sorted by `since` value. use std::collections::HashMap; use std::fmt; @@ -14,6 +15,14 @@ use std::fs::{self, File}; use std::io::prelude::*; use std::path::Path; +use regex::{Regex, escape}; + +mod version; +use self::version::Version; + +const FEATURE_GROUP_START_PREFIX: &str = "// feature-group-start"; +const FEATURE_GROUP_END_PREFIX: &str = "// feature-group-end"; + #[derive(Debug, PartialEq, Clone)] pub enum Status { Stable, @@ -35,7 +44,7 @@ impl fmt::Display for Status { #[derive(Debug, Clone)] pub struct Feature { pub level: Status, - pub since: String, + pub since: Option, pub has_gate_test: bool, pub tracking_issue: Option, } @@ -129,20 +138,8 @@ pub fn check(path: &Path, bad: &mut bool, quiet: bool) { } let mut lines = Vec::new(); - for (name, feature) in features.iter() { - lines.push(format!("{:<32} {:<8} {:<12} {:<8}", - name, - "lang", - feature.level, - feature.since)); - } - for (name, feature) in lib_features { - lines.push(format!("{:<32} {:<8} {:<12} {:<8}", - name, - "lib", - feature.level, - feature.since)); - } + lines.extend(format_features(&features, "lang")); + lines.extend(format_features(&lib_features, "lib")); lines.sort(); for line in lines { @@ -150,11 +147,31 @@ pub fn check(path: &Path, bad: &mut bool, quiet: bool) { } } +fn format_features<'a>(features: &'a Features, family: &'a str) -> impl Iterator + 'a { + features.iter().map(move |(name, feature)| { + format!("{:<32} {:<8} {:<12} {:<8}", + name, + family, + feature.level, + feature.since.map_or("None".to_owned(), + |since| since.to_string())) + }) +} + fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> { - line.find(attr) - .and_then(|i| line[i..].find('"').map(|j| i + j + 1)) - .and_then(|i| line[i..].find('"').map(|j| (i, i + j))) - .map(|(i, j)| &line[i..j]) + let r = Regex::new(&format!(r#"{}\s*=\s*"([^"]*)""#, escape(attr))) + .expect("malformed regex for find_attr_val"); + r.captures(line) + .and_then(|c| c.get(1)) + .map(|m| m.as_str()) +} + +#[test] +fn test_find_attr_val() { + let s = r#"#[unstable(feature = "checked_duration_since", issue = "58402")]"#; + assert_eq!(find_attr_val(s, "feature"), Some("checked_duration_since")); + assert_eq!(find_attr_val(s, "issue"), Some("58402")); + assert_eq!(find_attr_val(s, "since"), None); } fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool { @@ -177,6 +194,9 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features { // without one inside `// no tracking issue START` and `// no tracking issue END`. let mut next_feature_omits_tracking_issue = false; + let mut in_feature_group = false; + let mut prev_since = None; + contents.lines().zip(1..) .filter_map(|(line, line_number)| { let line = line.trim(); @@ -194,6 +214,25 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features { _ => {} } + if line.starts_with(FEATURE_GROUP_START_PREFIX) { + if in_feature_group { + tidy_error!( + bad, + // ignore-tidy-linelength + "libsyntax/feature_gate.rs:{}: new feature group is started without ending the previous one", + line_number, + ); + } + + in_feature_group = true; + prev_since = None; + return None; + } else if line.starts_with(FEATURE_GROUP_END_PREFIX) { + in_feature_group = false; + prev_since = None; + return None; + } + let mut parts = line.split(','); let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) { Some("active") => Status::Unstable, @@ -202,7 +241,33 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features { _ => return None, }; let name = parts.next().unwrap().trim(); - let since = parts.next().unwrap().trim().trim_matches('"'); + + let since_str = parts.next().unwrap().trim().trim_matches('"'); + let since = match since_str.parse() { + Ok(since) => Some(since), + Err(err) => { + tidy_error!( + bad, + "libsyntax/feature_gate.rs:{}: failed to parse since: {} ({:?})", + line_number, + since_str, + err, + ); + None + } + }; + if in_feature_group { + if prev_since > since { + tidy_error!( + bad, + "libsyntax/feature_gate.rs:{}: feature {} is not sorted by since", + line_number, + name, + ); + } + prev_since = since; + } + let issue_str = parts.next().unwrap().trim(); let tracking_issue = if issue_str.starts_with("None") { if level == Status::Unstable && !next_feature_omits_tracking_issue { @@ -222,7 +287,7 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features { Some((name.to_owned(), Feature { level, - since: since.to_owned(), + since, has_gate_test: false, tracking_issue, })) @@ -239,7 +304,7 @@ pub fn collect_lib_features(base_src_path: &Path) -> Features { // add it to the set of known library features so we can still generate docs. lib_features.insert("compiler_builtins_lib".to_owned(), Feature { level: Status::Unstable, - since: String::new(), + since: None, has_gate_test: false, tracking_issue: None, }); @@ -336,11 +401,11 @@ fn map_lib_features(base_src_path: &Path, // `const fn` features are handled specially. let feature_name = match find_attr_val(line, "feature") { Some(name) => name, - None => err!("malformed stability attribute"), + None => err!("malformed stability attribute: missing `feature` key"), }; let feature = Feature { level: Status::Unstable, - since: "None".to_owned(), + since: None, has_gate_test: false, // FIXME(#57563): #57563 is now used as a common tracking issue, // although we would like to have specific tracking issues for each @@ -359,20 +424,23 @@ fn map_lib_features(base_src_path: &Path, }; let feature_name = match find_attr_val(line, "feature") { Some(name) => name, - None => err!("malformed stability attribute"), + None => err!("malformed stability attribute: missing `feature` key"), }; - let since = match find_attr_val(line, "since") { - Some(name) => name, + let since = match find_attr_val(line, "since").map(|x| x.parse()) { + Some(Ok(since)) => Some(since), + Some(Err(_err)) => { + err!("malformed stability attribute: can't parse `since` key"); + }, None if level == Status::Stable => { - err!("malformed stability attribute"); + err!("malformed stability attribute: missing the `since` key"); } - None => "None", + None => None, }; let tracking_issue = find_attr_val(line, "issue").map(|s| s.parse().unwrap()); let feature = Feature { level, - since: since.to_owned(), + since, has_gate_test: false, tracking_issue, }; diff --git a/src/tools/tidy/src/features/version.rs b/src/tools/tidy/src/features/version.rs new file mode 100644 index 0000000000000..6027e7d35e28c --- /dev/null +++ b/src/tools/tidy/src/features/version.rs @@ -0,0 +1,92 @@ +use std::str::FromStr; +use std::num::ParseIntError; +use std::fmt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Version { + parts: [u32; 3], +} + +impl fmt::Display for Version { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad(&format!("{}.{}.{}", self.parts[0], self.parts[1], self.parts[2])) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum ParseVersionError { + ParseIntError(ParseIntError), + WrongNumberOfParts, +} + +impl From for ParseVersionError { + fn from(err: ParseIntError) -> Self { + ParseVersionError::ParseIntError(err) + } +} + +impl FromStr for Version { + type Err = ParseVersionError; + + fn from_str(s: &str) -> Result { + let mut iter = s.split('.').map(|part| Ok(part.parse()?)); + + let parts = { + let mut part = || { + iter.next() + .unwrap_or(Err(ParseVersionError::WrongNumberOfParts)) + }; + + [part()?, part()?, part()?] + }; + + if let Some(_) = iter.next() { + // Ensure we don't have more than 3 parts. + return Err(ParseVersionError::WrongNumberOfParts); + } + + Ok(Self { parts }) + } +} + +#[cfg(test)] +mod test { + use super::Version; + + #[test] + fn test_try_from_invalid_version() { + assert!("".parse::().is_err()); + assert!("hello".parse::().is_err()); + assert!("1.32.hi".parse::().is_err()); + assert!("1.32..1".parse::().is_err()); + assert!("1.32".parse::().is_err()); + assert!("1.32.0.1".parse::().is_err()); + } + + #[test] + fn test_try_from_single() { + assert_eq!("1.32.0".parse(), Ok(Version { parts: [1, 32, 0] })); + assert_eq!("1.0.0".parse(), Ok(Version { parts: [1, 0, 0] })); + } + + #[test] + fn test_compare() { + let v_1_0_0 = "1.0.0".parse::().unwrap(); + let v_1_32_0 = "1.32.0".parse::().unwrap(); + let v_1_32_1 = "1.32.1".parse::().unwrap(); + assert!(v_1_0_0 < v_1_32_1); + assert!(v_1_0_0 < v_1_32_0); + assert!(v_1_32_0 < v_1_32_1); + } + + #[test] + fn test_to_string() { + let v_1_0_0 = "1.0.0".parse::().unwrap(); + let v_1_32_1 = "1.32.1".parse::().unwrap(); + + assert_eq!(v_1_0_0.to_string(), "1.0.0"); + assert_eq!(v_1_32_1.to_string(), "1.32.1"); + assert_eq!(format!("{:<8}", v_1_32_1), "1.32.1 "); + assert_eq!(format!("{:>8}", v_1_32_1), " 1.32.1"); + } +} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index c4a1246ffdf55..30080452edc01 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -5,6 +5,7 @@ #![deny(rust_2018_idioms)] +extern crate regex; extern crate serde_json; #[macro_use] extern crate serde_derive;