diff --git a/Cargo.lock b/Cargo.lock index 86dd4695aa4fe..17378cb4a2d07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,7 +180,7 @@ dependencies = [ "merge", "num_cpus", "once_cell", - "opener", + "opener 0.5.0", "pretty_assertions", "serde", "serde_json", @@ -195,7 +195,9 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" dependencies = [ + "lazy_static", "memchr", + "regex-automata", ] [[package]] @@ -253,7 +255,7 @@ checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da" [[package]] name = "cargo" -version = "0.55.0" +version = "0.56.0" dependencies = [ "anyhow", "atty", @@ -288,11 +290,10 @@ dependencies = [ "log", "memchr", "num_cpus", - "opener", + "opener 0.5.0", "openssl", "percent-encoding 2.1.0", "pretty_env_logger", - "rand 0.8.3", "rustc-workspace-hack", "rustfix 0.6.0", "semver 1.0.3", @@ -375,10 +376,12 @@ dependencies = [ "flate2", "git2", "glob", + "itertools 0.10.0", "lazy_static", "remove_dir_all", "serde_json", "tar", + "termcolor", "toml", "url 2.2.2", ] @@ -575,7 +578,7 @@ dependencies = [ "bytecount", "clap", "itertools 0.9.0", - "opener", + "opener 0.4.1", "regex", "shell-escape", "walkdir", @@ -2418,6 +2421,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "opener" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952" +dependencies = [ + "bstr", + "winapi 0.3.9", +] + [[package]] name = "openssl" version = "0.10.33" @@ -3558,6 +3571,7 @@ dependencies = [ name = "rustc-workspace-hack" version = "1.0.0" dependencies = [ + "bstr", "byteorder", "crossbeam-utils 0.8.3", "libc", @@ -5158,9 +5172,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ "winapi-util", ] diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index b943977e4c2bb..87bc829b48891 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -528,8 +528,12 @@ fn stderr_isatty() -> bool { } fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { - let normalised = - if code.starts_with('E') { code.to_string() } else { format!("E{0:0>4}", code) }; + let upper_cased_code = code.to_ascii_uppercase(); + let normalised = if upper_cased_code.starts_with('E') { + upper_cased_code + } else { + format!("E{0:0>4}", code) + }; match registry.try_find_description(&normalised) { Ok(Some(description)) => { let mut is_in_code_block = false; diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index f10efd832361c..ff7a2344e6953 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -609,7 +609,7 @@ E0783: include_str!("./error_codes/E0783.md"), // E0540, // multiple rustc_deprecated attributes E0544, // multiple stability levels // E0548, // replaced with a generic attribute input check - E0553, // multiple rustc_const_unstable attributes +// E0553, // multiple rustc_const_unstable attributes // E0555, // replaced with a generic attribute input check // E0558, // replaced with a generic attribute input check // E0563, // cannot determine a type for this `impl Trait` removed in 6383de15 @@ -620,10 +620,9 @@ E0783: include_str!("./error_codes/E0783.md"), // E0612, // merged into E0609 // E0613, // Removed (merged with E0609) E0625, // thread-local statics cannot be accessed at compile-time - E0629, // missing 'feature' (rustc_const_unstable) - // rustc_const_unstable attribute must be paired with stable/unstable - // attribute - E0630, +// E0629, // missing 'feature' (rustc_const_unstable) +// E0630, // rustc_const_unstable attribute must be paired with stable/unstable + // attribute E0632, // cannot provide explicit generic arguments when `impl Trait` is // used in argument position E0640, // infer outlives requirements diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 6b2e542ee7051..cdefc9effa1e9 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -1,3 +1,13 @@ +//! THIR datatypes and definitions. See the [rustc dev guide] for more info. +//! +//! If you compare the THIR [`ExprKind`] to [`hir::ExprKind`], you will see it is +//! a good bit simpler. In fact, a number of the more straight-forward +//! MIR simplifications are already done in the lowering to THIR. For +//! example, method calls and overloaded operators are absent: they are +//! expected to be converted into [`ExprKind::Call`] instances. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/thir.html + use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir as hir; use rustc_hir::def::CtorKind; @@ -24,6 +34,7 @@ use std::fmt; use std::ops::Index; newtype_index! { + /// An index to an [`Arm`] stored in [`Thir::arms`] #[derive(HashStable)] pub struct ArmId { DEBUG_FORMAT = "a{}" @@ -31,6 +42,7 @@ newtype_index! { } newtype_index! { + /// An index to an [`Expr`] stored in [`Thir::exprs`] #[derive(HashStable)] pub struct ExprId { DEBUG_FORMAT = "e{}" @@ -39,6 +51,7 @@ newtype_index! { newtype_index! { #[derive(HashStable)] + /// An index to a [`Stmt`] stored in [`Thir::stmts`] pub struct StmtId { DEBUG_FORMAT = "s{}" } @@ -46,6 +59,9 @@ newtype_index! { macro_rules! thir_with_elements { ($($name:ident: $id:ty => $value:ty,)*) => { + /// A container for a THIR body. + /// + /// This can be indexed directly by any THIR index (e.g. [`ExprId`]). #[derive(Debug, HashStable)] pub struct Thir<'tcx> { $( @@ -88,18 +104,28 @@ pub enum LintLevel { #[derive(Debug, HashStable)] pub struct Block { + /// Whether the block itself has a label. Used by `label: {}` + /// and `try` blocks. + /// + /// This does *not* include labels on loops, e.g. `'label: loop {}`. pub targeted_by_break: bool, pub region_scope: region::Scope, pub opt_destruction_scope: Option, + /// The span of the block, including the opening braces, + /// the label, and the `unsafe` keyword, if present. pub span: Span, + /// The statements in the blocK. pub stmts: Box<[StmtId]>, + /// The trailing expression of the block, if any. pub expr: Option, pub safety_mode: BlockSafety, } #[derive(Debug, HashStable)] pub struct Adt<'tcx> { + /// The ADT we're constructing. pub adt_def: &'tcx AdtDef, + /// The variant of the ADT. pub variant_index: VariantIdx, pub substs: SubstsRef<'tcx>, @@ -108,13 +134,16 @@ pub struct Adt<'tcx> { pub user_ty: Option>>, pub fields: Box<[FieldExpr]>, + /// The base, e.g. `Foo {x: 1, .. base}`. pub base: Option>, } #[derive(Copy, Clone, Debug, HashStable)] pub enum BlockSafety { Safe, + /// A compiler-generated unsafe block BuiltinUnsafe, + /// An `unsafe` block. The `HirId` is the ID of the block. ExplicitUnsafe(hir::HirId), } @@ -126,32 +155,34 @@ pub struct Stmt<'tcx> { #[derive(Debug, HashStable)] pub enum StmtKind<'tcx> { + /// An expression with a trailing semicolon. Expr { - /// scope for this statement; may be used as lifetime of temporaries + /// The scope for this statement; may be used as lifetime of temporaries. scope: region::Scope, - /// expression being evaluated in this statement + /// The expression being evaluated in this statement. expr: ExprId, }, + /// A `let` binding. Let { - /// scope for variables bound in this let; covers this and - /// remaining statements in block + /// The scope for variables bound in this `let`; it covers this and + /// all the remaining statements in the block. remainder_scope: region::Scope, - /// scope for the initialization itself; might be used as - /// lifetime of temporaries + /// The scope for the initialization itself; might be used as + /// lifetime of temporaries. init_scope: region::Scope, /// `let = ...` /// - /// if a type is included, it is added as an ascription pattern + /// If a type annotation is included, it is added as an ascription pattern. pattern: Pat<'tcx>, - /// let pat: ty = ... + /// `let pat: ty = ` initializer: Option, - /// the lint level for this let-statement + /// The lint level for this `let` statement. lint_level: LintLevel, }, } @@ -160,27 +191,14 @@ pub enum StmtKind<'tcx> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Expr<'_>, 104); -/// The Thir trait implementor lowers their expressions (`&'tcx H::Expr`) -/// into instances of this `Expr` enum. This lowering can be done -/// basically as lazily or as eagerly as desired: every recursive -/// reference to an expression in this enum is an `ExprId`, which -/// may in turn be another instance of this enum (boxed), or else an -/// unlowered `&'tcx H::Expr`. Note that instances of `Expr` are very -/// short-lived. They are created by `Thir::to_expr`, analyzed and -/// converted into MIR, and then discarded. -/// -/// If you compare `Expr` to the full compiler AST, you will see it is -/// a good bit simpler. In fact, a number of the more straight-forward -/// MIR simplifications are already done in the impl of `Thir`. For -/// example, method calls and overloaded operators are absent: they are -/// expected to be converted into `Expr::Call` instances. +/// A THIR expression. #[derive(Debug, HashStable)] pub struct Expr<'tcx> { - /// type of this expression + /// The type of this expression pub ty: Ty<'tcx>, - /// lifetime of this expression if it should be spilled into a - /// temporary; should be None only if in a constant context + /// The lifetime of this expression if it should be spilled into a + /// temporary; should be `None` only if in a constant context pub temp_lifetime: Option, /// span of the expression in the source @@ -192,88 +210,120 @@ pub struct Expr<'tcx> { #[derive(Debug, HashStable)] pub enum ExprKind<'tcx> { + /// `Scope`s are used to explicitely mark destruction scopes, + /// and to track the `HirId` of the expressions within the scope. Scope { region_scope: region::Scope, lint_level: LintLevel, value: ExprId, }, + /// A `box ` expression. Box { value: ExprId, }, + /// An `if` expression. If { cond: ExprId, then: ExprId, else_opt: Option, }, + /// A function call. Method calls and overloaded operators are converted to plain function calls. Call { + /// The type of the function. This is often a [`FnDef`] or a [`FnPtr`]. + /// + /// [`FnDef`]: ty::TyKind::FnDef + /// [`FnPtr`]: ty::TyKind::FnPtr ty: Ty<'tcx>, + /// The function itself. fun: ExprId, + /// The arguments passed to the function. + /// + /// Note: in some cases (like calling a closure), the function call `f(...args)` gets + /// rewritten as a call to a function trait method (e.g. `FnOnce::call_once(f, (...args))`). args: Box<[ExprId]>, - /// Whether this is from a call in HIR, rather than from an overloaded - /// operator. `true` for overloaded function call. + /// Whether this is from an overloaded operator rather than a + /// function call from HIR. `true` for overloaded function call. from_hir_call: bool, - /// This `Span` is the span of the function, without the dot and receiver - /// (e.g. `foo(a, b)` in `x.foo(a, b)` + /// The span of the function, without the dot and receiver + /// (e.g. `foo(a, b)` in `x.foo(a, b)`). fn_span: Span, }, + /// A *non-overloaded* dereference. Deref { arg: ExprId, - }, // NOT overloaded! + }, + /// A *non-overloaded* binary operation. Binary { op: BinOp, lhs: ExprId, rhs: ExprId, - }, // NOT overloaded! + }, + /// A logical operation. This is distinct from `BinaryOp` because + /// the operands need to be lazily evaluated. LogicalOp { op: LogicalOp, lhs: ExprId, rhs: ExprId, - }, // NOT overloaded! - // LogicalOp is distinct from BinaryOp because of lazy evaluation of the operands. + }, + /// A *non-overloaded* unary operation. Note that here the deref (`*`) + /// operator is represented by `ExprKind::Deref`. Unary { op: UnOp, arg: ExprId, - }, // NOT overloaded! + }, + /// A cast: ` as `. The type we cast to is the type of + /// the parent expression. Cast { source: ExprId, }, Use { source: ExprId, }, // Use a lexpr to get a vexpr. + /// A coercion from `!` to any type. NeverToAny { source: ExprId, }, + /// A pointer cast. More information can be found in [`PointerCast`]. Pointer { cast: PointerCast, source: ExprId, }, + /// A `loop` expression. Loop { body: ExprId, }, + /// A `match` expression. Match { scrutinee: ExprId, arms: Box<[ArmId]>, }, + /// A block. Block { body: Block, }, + /// An assignment: `lhs = rhs`. Assign { lhs: ExprId, rhs: ExprId, }, + /// A *non-overloaded* operation assignment, e.g. `lhs += rhs`. AssignOp { op: BinOp, lhs: ExprId, rhs: ExprId, }, + /// Access to a struct or tuple field. Field { lhs: ExprId, + /// This can be a named (`.foo`) or unnamed (`.0`) field. name: Field, }, + /// A *non-overloaded* indexing operation. Index { lhs: ExprId, index: ExprId, }, + /// A local variable. VarRef { id: hir::HirId, }, @@ -285,6 +335,7 @@ pub enum ExprKind<'tcx> { /// HirId of the root variable var_hir_id: hir::HirId, }, + /// A borrow, e.g. `&arg`. Borrow { borrow_kind: BorrowKind, arg: ExprId, @@ -294,40 +345,51 @@ pub enum ExprKind<'tcx> { mutability: hir::Mutability, arg: ExprId, }, + /// A `break` expression. Break { label: region::Scope, value: Option, }, + /// A `continue` expression. Continue { label: region::Scope, }, + /// A `return` expression. Return { value: Option, }, + /// An inline `const` block, e.g. `const {}`. ConstBlock { value: &'tcx Const<'tcx>, }, + /// An array literal constructed from one repeated element, e.g. `[1; 5]`. Repeat { value: ExprId, count: &'tcx Const<'tcx>, }, + /// An array, e.g. `[a, b, c, d]`. Array { fields: Box<[ExprId]>, }, + /// A tuple, e.g. `(a, b, c, d)`. Tuple { fields: Box<[ExprId]>, }, + /// An ADT constructor, e.g. `Foo {x: 1, y: 2}`. Adt(Box>), + /// A type ascription on a place. PlaceTypeAscription { source: ExprId, /// Type that the user gave to this expression user_ty: Option>>, }, + /// A type ascription on a value, e.g. `42: i32`. ValueTypeAscription { source: ExprId, /// Type that the user gave to this expression user_ty: Option>>, }, + /// A closure definition. Closure { closure_id: DefId, substs: UpvarSubsts<'tcx>, @@ -335,6 +397,7 @@ pub enum ExprKind<'tcx> { movability: Option, fake_reads: Vec<(ExprId, FakeReadCause, hir::HirId)>, }, + /// A literal. Literal { literal: &'tcx Const<'tcx>, user_ty: Option>>, @@ -351,6 +414,7 @@ pub enum ExprKind<'tcx> { literal: &'tcx Const<'tcx>, def_id: DefId, }, + /// Inline assembly, i.e. `asm!()`. InlineAsm { template: &'tcx [InlineAsmTemplatePiece], operands: Box<[InlineAsmOperand<'tcx>]>, @@ -359,16 +423,21 @@ pub enum ExprKind<'tcx> { }, /// An expression taking a reference to a thread local. ThreadLocalRef(DefId), + /// Inline LLVM assembly, i.e. `llvm_asm!()`. LlvmInlineAsm { asm: &'tcx hir::LlvmInlineAsmInner, outputs: Box<[ExprId]>, inputs: Box<[ExprId]>, }, + /// A `yield` expression. Yield { value: ExprId, }, } +/// Represents the association of a field identifier and an expression. +/// +/// This is used in struct constructors. #[derive(Debug, HashStable)] pub struct FieldExpr { pub name: Field, @@ -381,6 +450,7 @@ pub struct FruInfo<'tcx> { pub field_types: Box<[Ty<'tcx>]>, } +/// A `match` arm. #[derive(Debug, HashStable)] pub struct Arm<'tcx> { pub pattern: Pat<'tcx>, @@ -391,6 +461,7 @@ pub struct Arm<'tcx> { pub span: Span, } +/// A `match` guard. #[derive(Debug, HashStable)] pub enum Guard<'tcx> { If(ExprId), @@ -399,7 +470,9 @@ pub enum Guard<'tcx> { #[derive(Copy, Clone, Debug, HashStable)] pub enum LogicalOp { + /// The `&&` operator. And, + /// The `||` operator. Or, } @@ -516,6 +589,7 @@ pub struct Ascription<'tcx> { #[derive(Clone, Debug, PartialEq, HashStable)] pub enum PatKind<'tcx> { + /// A wildward pattern: `_`. Wild, AscribeUserType { diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs index f8325a3646ff4..cb1203393b3d7 100644 --- a/compiler/rustc_mir/src/util/pretty.rs +++ b/compiler/rustc_mir/src/util/pretty.rs @@ -426,14 +426,14 @@ impl ExtraComments<'tcx> { } } -fn use_verbose(ty: &&TyS<'tcx>) -> bool { +fn use_verbose(ty: &&TyS<'tcx>, fn_def: bool) -> bool { match ty.kind() { ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false, // Unit type ty::Tuple(g_args) if g_args.is_empty() => false, - ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(&g_arg.expect_ty())), - ty::Array(ty, _) => use_verbose(ty), - ty::FnDef(..) => false, + ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(&g_arg.expect_ty(), fn_def)), + ty::Array(ty, _) => use_verbose(ty, fn_def), + ty::FnDef(..) => fn_def, _ => true, } } @@ -442,28 +442,20 @@ impl Visitor<'tcx> for ExtraComments<'tcx> { fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { self.super_constant(constant, location); let Constant { span, user_ty, literal } = constant; - match literal.ty().kind() { - ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char => {} - // Unit type - ty::Tuple(tys) if tys.is_empty() => {} - _ => { - self.push("mir::Constant"); - self.push(&format!( - "+ span: {}", - self.tcx.sess.source_map().span_to_embeddable_string(*span) - )); - if let Some(user_ty) = user_ty { - self.push(&format!("+ user_ty: {:?}", user_ty)); - } - match literal { - ConstantKind::Ty(literal) => self.push(&format!("+ literal: {:?}", literal)), - ConstantKind::Val(val, ty) => { - // To keep the diffs small, we render this almost like we render ty::Const - self.push(&format!( - "+ literal: Const {{ ty: {}, val: Value({:?}) }}", - ty, val - )) - } + if use_verbose(&literal.ty(), true) { + self.push("mir::Constant"); + self.push(&format!( + "+ span: {}", + self.tcx.sess.source_map().span_to_embeddable_string(*span) + )); + if let Some(user_ty) = user_ty { + self.push(&format!("+ user_ty: {:?}", user_ty)); + } + match literal { + ConstantKind::Ty(literal) => self.push(&format!("+ literal: {:?}", literal)), + ConstantKind::Val(val, ty) => { + // To keep the diffs small, we render this almost like we render ty::Const + self.push(&format!("+ literal: Const {{ ty: {}, val: Value({:?}) }}", ty, val)) } } } @@ -472,7 +464,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> { fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) { self.super_const(constant); let ty::Const { ty, val, .. } = constant; - if use_verbose(ty) { + if use_verbose(ty, false) { self.push("ty::Const"); self.push(&format!("+ ty: {:?}", ty)); let val = match val { diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 6baa185406e20..4c9c3954624f5 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -177,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We now fake capture information for all variables that are mentioned within the closure // We do this after handling migrations so that min_captures computes before - if !self.tcx.features().capture_disjoint_fields { + if !enable_precise_capture(self.tcx, span) { let mut capture_information: InferredCaptureInformation<'tcx> = Default::default(); if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { @@ -212,7 +212,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we have an origin, store it. if let Some(origin) = delegate.current_origin.clone() { - let origin = if self.tcx.features().capture_disjoint_fields { + let origin = if enable_precise_capture(self.tcx, span) { (origin.0, restrict_capture_precision(origin.1)) } else { (origin.0, Place { projections: vec![], ..origin.1 }) @@ -1924,3 +1924,13 @@ fn determine_place_ancestry_relation( PlaceAncestryRelation::Divergent } } + +/// Precise capture is enabled if the feature gate `capture_disjoint_fields` is enabled or if +/// user is using Rust Edition 2021 or higher. +/// +/// `span` is the span of the closure. +fn enable_precise_capture(tcx: TyCtxt<'_>, span: Span) -> bool { + // We use span here to ensure that if the closure was generated by a macro with a different + // edition. + tcx.features().capture_disjoint_fields || span.rust_2021() +} diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index c302502b3b7e6..6d3ab788e5f48 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -248,6 +248,11 @@ pub trait DoubleEndedIterator: Iterator { /// Folding is useful whenever you have a collection of something, and want /// to produce a single value from it. /// + /// Note: `rfold()` combines elements in a *right-associative* fashion. For associative + /// operators like `+`, the order the elements are combined in is not important, but for non-associative + /// operators like `-` the order will affect the final result. + /// For a *left-associative* version of `rfold()`, see [`Iterator::fold()`]. + /// /// # Examples /// /// Basic usage: @@ -262,7 +267,8 @@ pub trait DoubleEndedIterator: Iterator { /// assert_eq!(sum, 6); /// ``` /// - /// This example builds a string, starting with an initial value + /// This example demonstrates the right-associative nature of `rfold()`: + /// it builds a string, starting with an initial value /// and continuing with each element from the back until the front: /// /// ``` @@ -276,6 +282,7 @@ pub trait DoubleEndedIterator: Iterator { /// /// assert_eq!(result, "(1 + (2 + (3 + (4 + (5 + 0)))))"); /// ``` + #[doc(alias = "foldr")] #[inline] #[stable(feature = "iter_rfold", since = "1.27.0")] fn rfold(mut self, init: B, mut f: F) -> B diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 78d317096b4f6..41d9993abaa4f 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -2083,6 +2083,11 @@ pub trait Iterator { /// Note: [`reduce()`] can be used to use the first element as the initial /// value, if the accumulator type and item type is the same. /// + /// Note: `fold()` combines elements in a *left-associative* fashion. For associative + /// operators like `+`, the order the elements are combined in is not important, but for non-associative + /// operators like `-` the order will affect the final result. + /// For a *right-associative* version of `fold()`, see [`DoubleEndedIterator::rfold()`]. + /// /// # Note to Implementors /// /// Several of the other (forward) methods have default implementations in @@ -2116,6 +2121,21 @@ pub trait Iterator { /// /// And so, our final result, `6`. /// + /// This example demonstrates the left-associative nature of `fold()`: + /// it builds a string, starting with an initial value + /// and continuing with each element from the front until the back: + /// + /// ``` + /// let numbers = [1, 2, 3, 4, 5]; + /// + /// let zero = "0".to_string(); + /// + /// let result = numbers.iter().fold(zero, |acc, &x| { + /// format!("({} + {})", acc, x) + /// }); + /// + /// assert_eq!(result, "(((((0 + 1) + 2) + 3) + 4) + 5)"); + /// ``` /// It's common for people who haven't used iterators a lot to /// use a `for` loop with a list of things to build up a result. Those /// can be turned into `fold()`s: @@ -2140,7 +2160,7 @@ pub trait Iterator { /// ``` /// /// [`reduce()`]: Iterator::reduce - #[doc(alias = "inject")] + #[doc(alias = "inject", alias = "foldl")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn fold(mut self, init: B, mut f: F) -> B diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index d1e666936f88a..6fb7a1c088f33 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -48,7 +48,7 @@ toml = "0.5" lazy_static = "1.3.0" time = "0.1" ignore = "0.4.10" -opener = "0.4" +opener = "0.5" merge = "0.1.0" once_cell = "1.7.2" diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8c922240c4494..a43a5424f04fc 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -101,28 +101,6 @@ crate struct RenderType { generics: Option>, } -impl Serialize for RenderType { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - if let Some(name) = &self.name { - let mut seq = serializer.serialize_seq(None)?; - if let Some(id) = self.idx { - seq.serialize_element(&id)?; - } else { - seq.serialize_element(&name)?; - } - if let Some(generics) = &self.generics { - seq.serialize_element(&generics)?; - } - seq.end() - } else { - serializer.serialize_none() - } - } -} - /// A type used for the search index. #[derive(Debug)] crate struct Generic { diff --git a/src/test/mir-opt/const_prop/checked_add.main.ConstProp.diff b/src/test/mir-opt/const_prop/checked_add.main.ConstProp.diff index bccfa9da61530..77ff8ef4e4952 100644 --- a/src/test/mir-opt/const_prop/checked_add.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/checked_add.main.ConstProp.diff @@ -14,9 +14,6 @@ - _2 = CheckedAdd(const 1_u32, const 1_u32); // scope 0 at $DIR/checked_add.rs:5:18: 5:23 - assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 1_u32, const 1_u32) -> bb1; // scope 0 at $DIR/checked_add.rs:5:18: 5:23 + _2 = const (2_u32, false); // scope 0 at $DIR/checked_add.rs:5:18: 5:23 -+ // mir::Constant -+ // + span: $DIR/checked_add.rs:5:18: 5:23 -+ // + literal: Const { ty: (u32, bool), val: Value(ByRef { alloc: Allocation { bytes: [2, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 1_u32, const 1_u32) -> bb1; // scope 0 at $DIR/checked_add.rs:5:18: 5:23 } diff --git a/src/test/mir-opt/const_prop/indirect.main.ConstProp.diff b/src/test/mir-opt/const_prop/indirect.main.ConstProp.diff index 57a00ba12b0eb..8dd55235ef37b 100644 --- a/src/test/mir-opt/const_prop/indirect.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/indirect.main.ConstProp.diff @@ -18,9 +18,6 @@ - assert(!move (_3.1: bool), "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> bb1; // scope 0 at $DIR/indirect.rs:5:13: 5:29 + _2 = const 2_u8; // scope 0 at $DIR/indirect.rs:5:13: 5:25 + _3 = const (3_u8, false); // scope 0 at $DIR/indirect.rs:5:13: 5:29 -+ // mir::Constant -+ // + span: $DIR/indirect.rs:5:13: 5:29 -+ // + literal: Const { ty: (u8, bool), val: Value(ByRef { alloc: Allocation { bytes: [3, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_u8, const 1_u8) -> bb1; // scope 0 at $DIR/indirect.rs:5:13: 5:29 } diff --git a/src/test/mir-opt/const_prop/issue_67019.main.ConstProp.diff b/src/test/mir-opt/const_prop/issue_67019.main.ConstProp.diff index 518974e24b389..2d3289f7ce51d 100644 --- a/src/test/mir-opt/const_prop/issue_67019.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/issue_67019.main.ConstProp.diff @@ -15,9 +15,6 @@ (_3.1: u8) = const 2_u8; // scope 0 at $DIR/issue-67019.rs:11:11: 11:17 - (_2.0: (u8, u8)) = move _3; // scope 0 at $DIR/issue-67019.rs:11:10: 11:19 + (_2.0: (u8, u8)) = const (1_u8, 2_u8); // scope 0 at $DIR/issue-67019.rs:11:10: 11:19 -+ // mir::Constant -+ // + span: $DIR/issue-67019.rs:11:10: 11:19 -+ // + literal: Const { ty: (u8, u8), val: Value(ByRef { alloc: Allocation { bytes: [1, 2], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } StorageDead(_3); // scope 0 at $DIR/issue-67019.rs:11:18: 11:19 _1 = test(move _2) -> bb1; // scope 0 at $DIR/issue-67019.rs:11:5: 11:20 // mir::Constant diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff index d94c4f6fb26b5..a044d1dcfe1d9 100644 --- a/src/test/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff @@ -20,9 +20,6 @@ StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10 - _2 = _1; // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14 + _2 = const (42_i32, 99_i32); // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14 -+ // mir::Constant -+ // + span: $DIR/mutable_variable_aggregate.rs:7:13: 7:14 -+ // + literal: Const { ty: (i32, i32), val: Value(ByRef { alloc: Allocation { bytes: [42, 0, 0, 0, 99, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } nop; // scope 0 at $DIR/mutable_variable_aggregate.rs:4:11: 8:2 StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2 StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2 diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff index 537f1b6253ca0..4c3f66cd0907f 100644 --- a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff +++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff @@ -27,9 +27,6 @@ - _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + _2 = const (4_i32, false); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // + literal: Const { ty: (i32, bool), val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 } diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff index 537f1b6253ca0..4c3f66cd0907f 100644 --- a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff +++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff @@ -27,9 +27,6 @@ - _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 - assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 + _2 = const (4_i32, false); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // mir::Constant -+ // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18 -+ // + literal: Const { ty: (i32, bool), val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18 } diff --git a/src/test/mir-opt/const_prop/return_place.add.ConstProp.diff b/src/test/mir-opt/const_prop/return_place.add.ConstProp.diff index 97808246dd4bc..c6ac8d6fb139d 100644 --- a/src/test/mir-opt/const_prop/return_place.add.ConstProp.diff +++ b/src/test/mir-opt/const_prop/return_place.add.ConstProp.diff @@ -9,9 +9,6 @@ - _1 = CheckedAdd(const 2_u32, const 2_u32); // scope 0 at $DIR/return_place.rs:6:5: 6:10 - assert(!move (_1.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_u32, const 2_u32) -> bb1; // scope 0 at $DIR/return_place.rs:6:5: 6:10 + _1 = const (4_u32, false); // scope 0 at $DIR/return_place.rs:6:5: 6:10 -+ // mir::Constant -+ // + span: $DIR/return_place.rs:6:5: 6:10 -+ // + literal: Const { ty: (u32, bool), val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_u32, const 2_u32) -> bb1; // scope 0 at $DIR/return_place.rs:6:5: 6:10 } diff --git a/src/test/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.diff b/src/test/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.diff index b8f9cd34c99cb..15253a364e990 100644 --- a/src/test/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.diff @@ -18,9 +18,6 @@ StorageLive(_3); // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 - _3 = _1; // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 + _3 = const (1_u32, 2_u32); // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14 -+ // mir::Constant -+ // + span: $DIR/tuple_literal_propagation.rs:5:13: 5:14 -+ // + literal: Const { ty: (u32, u32), val: Value(ByRef { alloc: Allocation { bytes: [1, 0, 0, 0, 2, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } _2 = consume(move _3) -> bb1; // scope 1 at $DIR/tuple_literal_propagation.rs:5:5: 5:15 // mir::Constant // + span: $DIR/tuple_literal_propagation.rs:5:5: 5:12 diff --git a/src/test/mir-opt/deaggregator_test.bar.Deaggregator.diff b/src/test/mir-opt/deaggregator_test.bar.Deaggregator.diff index e60a1f3e75f9f..d3c7136c6478f 100644 --- a/src/test/mir-opt/deaggregator_test.bar.Deaggregator.diff +++ b/src/test/mir-opt/deaggregator_test.bar.Deaggregator.diff @@ -12,9 +12,6 @@ - _0 = Baz { x: move _2, y: const 0f32, z: const false }; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 + (_0.0: usize) = move _2; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 + (_0.1: f32) = const 0f32; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 - // mir::Constant - // + span: $DIR/deaggregator_test.rs:9:20: 9:23 - // + literal: Const { ty: f32, val: Value(Scalar(0x00000000)) } + (_0.2: bool) = const false; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35 StorageDead(_2); // scope 0 at $DIR/deaggregator_test.rs:9:34: 9:35 return; // scope 0 at $DIR/deaggregator_test.rs:10:2: 10:2 diff --git a/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff index 2c4952402a49d..64a3f52f3a06a 100644 --- a/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff +++ b/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff @@ -12,9 +12,6 @@ StorageLive(_3); // scope 0 at $DIR/if-condition-int.rs:53:8: 53:9 _3 = _1; // scope 0 at $DIR/if-condition-int.rs:53:8: 53:9 _2 = Eq(move _3, const -42f32); // scope 0 at $DIR/if-condition-int.rs:53:8: 53:18 - // mir::Constant - // + span: $DIR/if-condition-int.rs:53:13: 53:18 - // + literal: Const { ty: f32, val: Value(Scalar(0xc2280000)) } StorageDead(_3); // scope 0 at $DIR/if-condition-int.rs:53:17: 53:18 switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:53:5: 53:35 } diff --git a/src/test/mir-opt/simplify_locals_removes_unused_consts.main.SimplifyLocals.diff b/src/test/mir-opt/simplify_locals_removes_unused_consts.main.SimplifyLocals.diff index a698d8abcdb5a..598e8247efc5a 100644 --- a/src/test/mir-opt/simplify_locals_removes_unused_consts.main.SimplifyLocals.diff +++ b/src/test/mir-opt/simplify_locals_removes_unused_consts.main.SimplifyLocals.diff @@ -38,9 +38,6 @@ // mir::Constant // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:12 // + literal: Const { ty: fn(((), ())) {use_zst}, val: Value(Scalar()) } - // mir::Constant - // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22 - // + literal: Const { ty: ((), ()), val: Value(Scalar()) } } bb1: { diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs new file mode 100644 index 0000000000000..20bbe1d89e45d --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs @@ -0,0 +1,23 @@ +// edition:2021 +// run-pass + +// Test that edition 2021 enables disjoint capture by default. + +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 10 }; + + let c = || { + println!("{}", p.x); + }; + + // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. + let py = &mut p.y; + + c(); + *py = 20; +} diff --git a/src/tools/cargo b/src/tools/cargo index 44456677b5d1d..9233aa06c8018 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 44456677b5d1d82fe981c955dc5c67734b31f340 +Subproject commit 9233aa06c801801cff75df65df718d70905a235e diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml index d39cb597b21c8..4a4b26da54caf 100644 --- a/src/tools/rustc-workspace-hack/Cargo.toml +++ b/src/tools/rustc-workspace-hack/Cargo.toml @@ -61,6 +61,7 @@ features = [ ] [dependencies] +bstr = { version = "0.2.13", features = ["default"] } byteorder = { version = "1", features = ['default', 'std'] } curl-sys = { version = "0.4.13", features = ["http2", "libnghttp2-sys"], optional = true } crossbeam-utils = { version = "0.8.0", features = ["nightly"] } diff --git a/src/tools/tidy/src/error_codes_check.rs b/src/tools/tidy/src/error_codes_check.rs index d6e0ebaa5410c..63fbee34bd6e4 100644 --- a/src/tools/tidy/src/error_codes_check.rs +++ b/src/tools/tidy/src/error_codes_check.rs @@ -6,20 +6,33 @@ use std::ffi::OsStr; use std::fs::read_to_string; use std::path::Path; +use regex::Regex; + // A few of those error codes can't be tested but all the others can and *should* be tested! const EXEMPTED_FROM_TEST: &[&str] = &[ - "E0227", "E0279", "E0280", "E0313", "E0314", "E0315", "E0377", "E0461", "E0462", "E0464", - "E0465", "E0473", "E0474", "E0475", "E0476", "E0479", "E0480", "E0481", "E0482", "E0483", - "E0484", "E0485", "E0486", "E0487", "E0488", "E0489", "E0514", "E0519", "E0523", "E0553", - "E0554", "E0570", "E0629", "E0630", "E0640", "E0717", "E0729", + "E0227", "E0279", "E0280", "E0313", "E0377", "E0461", "E0462", "E0464", "E0465", "E0476", + "E0482", "E0514", "E0519", "E0523", "E0554", "E0570", "E0640", "E0717", "E0729", ]; // Some error codes don't have any tests apparently... const IGNORE_EXPLANATION_CHECK: &[&str] = &["E0570", "E0601", "E0602", "E0729"]; +// If the file path contains any of these, we don't want to try to extract error codes from it. +// +// We need to declare each path in the windows version (with backslash). +const PATHS_TO_IGNORE_FOR_EXTRACTION: &[&str] = + &["src/test/", "src\\test\\", "src/doc/", "src\\doc\\", "src/tools/", "src\\tools\\"]; + +#[derive(Default, Debug)] +struct ErrorCodeStatus { + has_test: bool, + has_explanation: bool, + is_used: bool, +} + fn check_error_code_explanation( f: &str, - error_codes: &mut HashMap, + error_codes: &mut HashMap, err_code: String, ) -> bool { let mut invalid_compile_fail_format = false; @@ -30,7 +43,7 @@ fn check_error_code_explanation( if s.starts_with("```") { if s.contains("compile_fail") && s.contains('E') { if !found_error_code { - error_codes.insert(err_code.clone(), true); + error_codes.get_mut(&err_code).map(|x| x.has_test = true); found_error_code = true; } } else if s.contains("compile-fail") { @@ -38,7 +51,7 @@ fn check_error_code_explanation( } } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") { if !found_error_code { - error_codes.get_mut(&err_code).map(|x| *x = true); + error_codes.get_mut(&err_code).map(|x| x.has_test = true); found_error_code = true; } } @@ -77,7 +90,7 @@ macro_rules! some_or_continue { fn extract_error_codes( f: &str, - error_codes: &mut HashMap, + error_codes: &mut HashMap, path: &Path, errors: &mut Vec, ) { @@ -90,15 +103,16 @@ fn extract_error_codes( .split_once(':') .expect( format!( - "Expected a line with the format `E0xxx: include_str!(\"..\")`, but got {} without a `:` delimiter", + "Expected a line with the format `E0xxx: include_str!(\"..\")`, but got {} \ + without a `:` delimiter", s, - ).as_str() + ) + .as_str(), ) .0 .to_owned(); - if !error_codes.contains_key(&err_code) { - error_codes.insert(err_code.clone(), false); - } + error_codes.entry(err_code.clone()).or_default().has_explanation = true; + // Now we extract the tests from the markdown file! let md_file_name = match s.split_once("include_str!(\"") { None => continue, @@ -145,7 +159,7 @@ fn extract_error_codes( .to_string(); if !error_codes.contains_key(&err_code) { // this check should *never* fail! - error_codes.insert(err_code, false); + error_codes.insert(err_code, ErrorCodeStatus::default()); } } else if s == ";" { reached_no_explanation = true; @@ -153,7 +167,7 @@ fn extract_error_codes( } } -fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap) { +fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap) { for line in f.lines() { let s = line.trim(); if s.starts_with("error[E") || s.starts_with("warning[E") { @@ -164,8 +178,24 @@ fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap err_code, }, }; - let nb = error_codes.entry(err_code.to_owned()).or_insert(false); - *nb = true; + error_codes.entry(err_code.to_owned()).or_default().has_test = true; + } + } +} + +fn extract_error_codes_from_source( + f: &str, + error_codes: &mut HashMap, + regex: &Regex, +) { + for line in f.lines() { + if line.trim_start().starts_with("//") { + continue; + } + for cap in regex.captures_iter(line) { + if let Some(error_code) = cap.get(1) { + error_codes.entry(error_code.as_str().to_owned()).or_default().is_used = true; + } } } } @@ -174,8 +204,17 @@ pub fn check(paths: &[&Path], bad: &mut bool) { let mut errors = Vec::new(); let mut found_explanations = 0; let mut found_tests = 0; + let mut error_codes: HashMap = HashMap::new(); + // We want error codes which match the following cases: + // + // * foo(a, E0111, a) + // * foo(a, E0111) + // * foo(E0111, a) + // * #[error = "E0111"] + let regex = Regex::new(r#"[(,"\s](E\d{4})[,)"]"#).unwrap(); + println!("Checking which error codes lack tests..."); - let mut error_codes: HashMap = HashMap::new(); + for path in paths { super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| { let file_name = entry.file_name(); @@ -185,6 +224,11 @@ pub fn check(paths: &[&Path], bad: &mut bool) { } else if entry.path().extension() == Some(OsStr::new("stderr")) { extract_error_codes_from_tests(contents, &mut error_codes); found_tests += 1; + } else if entry.path().extension() == Some(OsStr::new("rs")) { + let path = entry.path().to_string_lossy(); + if PATHS_TO_IGNORE_FOR_EXTRACTION.iter().all(|c| !path.contains(c)) { + extract_error_codes_from_source(contents, &mut error_codes, ®ex); + } } }); } @@ -199,15 +243,43 @@ pub fn check(paths: &[&Path], bad: &mut bool) { if errors.is_empty() { println!("Found {} error codes", error_codes.len()); - for (err_code, nb) in &error_codes { - if !*nb && !EXEMPTED_FROM_TEST.contains(&err_code.as_str()) { + for (err_code, error_status) in &error_codes { + if !error_status.has_test && !EXEMPTED_FROM_TEST.contains(&err_code.as_str()) { errors.push(format!("Error code {} needs to have at least one UI test!", err_code)); - } else if *nb && EXEMPTED_FROM_TEST.contains(&err_code.as_str()) { + } else if error_status.has_test && EXEMPTED_FROM_TEST.contains(&err_code.as_str()) { errors.push(format!( "Error code {} has a UI test, it shouldn't be listed into EXEMPTED_FROM_TEST!", err_code )); } + if !error_status.is_used && !error_status.has_explanation { + errors.push(format!( + "Error code {} isn't used and doesn't have an error explanation, it should be \ + commented in error_codes.rs file", + err_code + )); + } + } + } + if errors.is_empty() { + // Checking if local constants need to be cleaned. + for err_code in EXEMPTED_FROM_TEST { + match error_codes.get(err_code.to_owned()) { + Some(status) => { + if status.has_test { + errors.push(format!( + "{} error code has a test and therefore should be \ + removed from the `EXEMPTED_FROM_TEST` constant", + err_code + )); + } + } + None => errors.push(format!( + "{} error code isn't used anymore and therefore should be removed \ + from `EXEMPTED_FROM_TEST` constant", + err_code + )), + } } } errors.sort();