Auto merge of #86588 - JohnTitor:rollup-ibgjbkf, r=JohnTitor
Rollup of 8 pull requests Successful merges: - #86137 (Error code cleanup and enforce checks) - #86296 (Add documentation for various THIR structs) - #86415 (Document associativity of iterator folds.) - #86533 (Support lowercase error codes in `--explain`) - #86536 (Edition 2021 enables disjoint capture) - #86560 (Update cargo) - #86561 (chore(rustdoc): Remove unused impl block) - #86566 (Use `use_verbose` for `mir::Constant`) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
964a81eb37
28
Cargo.lock
28
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",
|
||||
]
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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<region::Scope>,
|
||||
/// 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<ExprId>,
|
||||
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<Canonical<'tcx, UserType<'tcx>>>,
|
||||
|
||||
pub fields: Box<[FieldExpr]>,
|
||||
/// The base, e.g. `Foo {x: 1, .. base}`.
|
||||
pub base: Option<FruInfo<'tcx>>,
|
||||
}
|
||||
|
||||
#[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 <PAT> = ...`
|
||||
///
|
||||
/// 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 = <INIT> ...
|
||||
/// `let pat: ty = <INIT>`
|
||||
initializer: Option<ExprId>,
|
||||
|
||||
/// 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<region::Scope>,
|
||||
|
||||
/// 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 <value>` expression.
|
||||
Box {
|
||||
value: ExprId,
|
||||
},
|
||||
/// An `if` expression.
|
||||
If {
|
||||
cond: ExprId,
|
||||
then: ExprId,
|
||||
else_opt: Option<ExprId>,
|
||||
},
|
||||
/// 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: `<source> as <type>`. 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<ExprId>,
|
||||
},
|
||||
/// A `continue` expression.
|
||||
Continue {
|
||||
label: region::Scope,
|
||||
},
|
||||
/// A `return` expression.
|
||||
Return {
|
||||
value: Option<ExprId>,
|
||||
},
|
||||
/// 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<Adt<'tcx>>),
|
||||
/// A type ascription on a place.
|
||||
PlaceTypeAscription {
|
||||
source: ExprId,
|
||||
/// Type that the user gave to this expression
|
||||
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
|
||||
},
|
||||
/// A type ascription on a value, e.g. `42: i32`.
|
||||
ValueTypeAscription {
|
||||
source: ExprId,
|
||||
/// Type that the user gave to this expression
|
||||
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
|
||||
},
|
||||
/// A closure definition.
|
||||
Closure {
|
||||
closure_id: DefId,
|
||||
substs: UpvarSubsts<'tcx>,
|
||||
@ -335,6 +397,7 @@ pub enum ExprKind<'tcx> {
|
||||
movability: Option<hir::Movability>,
|
||||
fake_reads: Vec<(ExprId, FakeReadCause, hir::HirId)>,
|
||||
},
|
||||
/// A literal.
|
||||
Literal {
|
||||
literal: &'tcx Const<'tcx>,
|
||||
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
|
||||
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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<B, F>(mut self, init: B, mut f: F) -> B
|
||||
|
@ -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<B, F>(mut self, init: B, mut f: F) -> B
|
||||
|
@ -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"
|
||||
|
||||
|
@ -101,28 +101,6 @@ crate struct RenderType {
|
||||
generics: Option<Vec<Generic>>,
|
||||
}
|
||||
|
||||
impl Serialize for RenderType {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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(<ZST>)) }
|
||||
// mir::Constant
|
||||
// + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22
|
||||
// + literal: Const { ty: ((), ()), val: Value(Scalar(<ZST>)) }
|
||||
}
|
||||
|
||||
bb1: {
|
||||
|
@ -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;
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit 44456677b5d1d82fe981c955dc5c67734b31f340
|
||||
Subproject commit 9233aa06c801801cff75df65df718d70905a235e
|
@ -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"] }
|
||||
|
@ -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<String, bool>,
|
||||
error_codes: &mut HashMap<String, ErrorCodeStatus>,
|
||||
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<String, bool>,
|
||||
error_codes: &mut HashMap<String, ErrorCodeStatus>,
|
||||
path: &Path,
|
||||
errors: &mut Vec<String>,
|
||||
) {
|
||||
@ -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<String, bool>) {
|
||||
fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, ErrorCodeStatus>) {
|
||||
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<String, boo
|
||||
Some((_, err_code)) => 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<String, ErrorCodeStatus>,
|
||||
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<String, ErrorCodeStatus> = 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<String, bool> = 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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user