Auto merge of #86502 - JohnTitor:rollup-wge0f3x, r=JohnTitor
Rollup of 8 pull requests Successful merges: - #83739 (Account for bad placeholder errors on consts/statics with trait objects) - #85637 (document PartialEq, PartialOrd, Ord requirements more explicitly) - #86152 (Lazify is_really_default condition in the RustdocGUI bootstrap step) - #86156 (Fix a bug in the linkchecker) - #86427 (Updated release note) - #86452 (fix panic-safety in specialized Zip::next_back) - #86484 (Do not set depth to 0 in fully_expand_fragment) - #86491 (expand: Move some more derive logic to rustc_builtin_macros) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
6a540bd40c
@ -179,6 +179,7 @@ dependencies = [
|
||||
"libc",
|
||||
"merge",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"opener",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
|
@ -1749,6 +1749,7 @@ Language
|
||||
- [You can now use `#[repr(transparent)]` on univariant `enum`s.][68122] Meaning
|
||||
that you can create an enum that has the exact layout and ABI of the type
|
||||
it contains.
|
||||
- [You can now use outer attribute procedural macros on inline modules.][64273]
|
||||
- [There are some *syntax-only* changes:][67131]
|
||||
- `default` is syntactically allowed before items in `trait` definitions.
|
||||
- Items in `impl`s (i.e. `const`s, `type`s, and `fn`s) may syntactically
|
||||
@ -1810,6 +1811,7 @@ Compatibility Notes
|
||||
[67935]: https://github.com/rust-lang/rust/pull/67935/
|
||||
[68339]: https://github.com/rust-lang/rust/pull/68339/
|
||||
[68122]: https://github.com/rust-lang/rust/pull/68122/
|
||||
[64273]: https://github.com/rust-lang/rust/pull/64273/
|
||||
[67712]: https://github.com/rust-lang/rust/pull/67712/
|
||||
[67887]: https://github.com/rust-lang/rust/pull/67887/
|
||||
[67131]: https://github.com/rust-lang/rust/pull/67131/
|
||||
|
@ -24,61 +24,60 @@ crate fn expand(
|
||||
annotatable: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval);
|
||||
cfg_eval(ecx, annotatable)
|
||||
vec![cfg_eval(ecx, annotatable)]
|
||||
}
|
||||
|
||||
crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Vec<Annotatable> {
|
||||
let mut visitor = CfgEval {
|
||||
crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Annotatable {
|
||||
CfgEval {
|
||||
cfg: &mut StripUnconfigured {
|
||||
sess: ecx.sess,
|
||||
features: ecx.ecfg.features,
|
||||
config_tokens: true,
|
||||
},
|
||||
};
|
||||
let annotatable = visitor.configure_annotatable(annotatable);
|
||||
vec![annotatable]
|
||||
}
|
||||
.configure_annotatable(annotatable)
|
||||
// Since the item itself has already been configured by the `InvocationCollector`,
|
||||
// we know that fold result vector will contain exactly one element.
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
struct CfgEval<'a, 'b> {
|
||||
cfg: &'a mut StripUnconfigured<'b>,
|
||||
}
|
||||
|
||||
fn flat_map_annotatable(vis: &mut impl MutVisitor, annotatable: Annotatable) -> Annotatable {
|
||||
// Since the item itself has already been configured by the InvocationCollector,
|
||||
// we know that fold result vector will contain exactly one element
|
||||
fn flat_map_annotatable(
|
||||
vis: &mut impl MutVisitor,
|
||||
annotatable: Annotatable,
|
||||
) -> Option<Annotatable> {
|
||||
match annotatable {
|
||||
Annotatable::Item(item) => Annotatable::Item(vis.flat_map_item(item).pop().unwrap()),
|
||||
Annotatable::Item(item) => vis.flat_map_item(item).pop().map(Annotatable::Item),
|
||||
Annotatable::TraitItem(item) => {
|
||||
Annotatable::TraitItem(vis.flat_map_trait_item(item).pop().unwrap())
|
||||
vis.flat_map_trait_item(item).pop().map(Annotatable::TraitItem)
|
||||
}
|
||||
Annotatable::ImplItem(item) => {
|
||||
Annotatable::ImplItem(vis.flat_map_impl_item(item).pop().unwrap())
|
||||
vis.flat_map_impl_item(item).pop().map(Annotatable::ImplItem)
|
||||
}
|
||||
Annotatable::ForeignItem(item) => {
|
||||
Annotatable::ForeignItem(vis.flat_map_foreign_item(item).pop().unwrap())
|
||||
vis.flat_map_foreign_item(item).pop().map(Annotatable::ForeignItem)
|
||||
}
|
||||
Annotatable::Stmt(stmt) => {
|
||||
Annotatable::Stmt(stmt.map(|stmt| vis.flat_map_stmt(stmt).pop().unwrap()))
|
||||
vis.flat_map_stmt(stmt.into_inner()).pop().map(P).map(Annotatable::Stmt)
|
||||
}
|
||||
Annotatable::Expr(mut expr) => Annotatable::Expr({
|
||||
Annotatable::Expr(mut expr) => {
|
||||
vis.visit_expr(&mut expr);
|
||||
expr
|
||||
}),
|
||||
Annotatable::Arm(arm) => Annotatable::Arm(vis.flat_map_arm(arm).pop().unwrap()),
|
||||
Some(Annotatable::Expr(expr))
|
||||
}
|
||||
Annotatable::Arm(arm) => vis.flat_map_arm(arm).pop().map(Annotatable::Arm),
|
||||
Annotatable::ExprField(field) => {
|
||||
Annotatable::ExprField(vis.flat_map_expr_field(field).pop().unwrap())
|
||||
}
|
||||
Annotatable::PatField(fp) => {
|
||||
Annotatable::PatField(vis.flat_map_pat_field(fp).pop().unwrap())
|
||||
vis.flat_map_expr_field(field).pop().map(Annotatable::ExprField)
|
||||
}
|
||||
Annotatable::PatField(fp) => vis.flat_map_pat_field(fp).pop().map(Annotatable::PatField),
|
||||
Annotatable::GenericParam(param) => {
|
||||
Annotatable::GenericParam(vis.flat_map_generic_param(param).pop().unwrap())
|
||||
vis.flat_map_generic_param(param).pop().map(Annotatable::GenericParam)
|
||||
}
|
||||
Annotatable::Param(param) => Annotatable::Param(vis.flat_map_param(param).pop().unwrap()),
|
||||
Annotatable::FieldDef(sf) => {
|
||||
Annotatable::FieldDef(vis.flat_map_field_def(sf).pop().unwrap())
|
||||
}
|
||||
Annotatable::Variant(v) => Annotatable::Variant(vis.flat_map_variant(v).pop().unwrap()),
|
||||
Annotatable::Param(param) => vis.flat_map_param(param).pop().map(Annotatable::Param),
|
||||
Annotatable::FieldDef(sf) => vis.flat_map_field_def(sf).pop().map(Annotatable::FieldDef),
|
||||
Annotatable::Variant(v) => vis.flat_map_variant(v).pop().map(Annotatable::Variant),
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,11 +122,11 @@ impl CfgEval<'_, '_> {
|
||||
self.cfg.configure(node)
|
||||
}
|
||||
|
||||
pub fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Annotatable {
|
||||
fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Option<Annotatable> {
|
||||
// Tokenizing and re-parsing the `Annotatable` can have a significant
|
||||
// performance impact, so try to avoid it if possible
|
||||
if !CfgFinder::has_cfg_or_cfg_attr(&annotatable) {
|
||||
return annotatable;
|
||||
return Some(annotatable);
|
||||
}
|
||||
|
||||
// The majority of parsed attribute targets will never need to have early cfg-expansion
|
||||
|
@ -26,6 +26,8 @@ impl MultiItemModifier for Expander {
|
||||
return ExpandResult::Ready(vec![item]);
|
||||
}
|
||||
|
||||
let item = cfg_eval(ecx, item);
|
||||
|
||||
let result =
|
||||
ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| {
|
||||
let template =
|
||||
@ -54,12 +56,12 @@ impl MultiItemModifier for Expander {
|
||||
report_path_args(sess, &meta);
|
||||
meta.path
|
||||
})
|
||||
.map(|path| (path, None))
|
||||
.map(|path| (path, item.clone(), None))
|
||||
.collect()
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(()) => ExpandResult::Ready(cfg_eval(ecx, item)),
|
||||
Ok(()) => ExpandResult::Ready(vec![item]),
|
||||
Err(Indeterminate) => ExpandResult::Retry(item),
|
||||
}
|
||||
}
|
||||
|
@ -835,7 +835,7 @@ impl SyntaxExtension {
|
||||
/// Error type that denotes indeterminacy.
|
||||
pub struct Indeterminate;
|
||||
|
||||
pub type DeriveResolutions = Vec<(ast::Path, Option<Lrc<SyntaxExtension>>)>;
|
||||
pub type DeriveResolutions = Vec<(ast::Path, Annotatable, Option<Lrc<SyntaxExtension>>)>;
|
||||
|
||||
pub trait ResolverExpand {
|
||||
fn next_node_id(&mut self) -> NodeId;
|
||||
|
@ -427,7 +427,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||
pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragment {
|
||||
let orig_expansion_data = self.cx.current_expansion.clone();
|
||||
let orig_force_mode = self.cx.force_mode;
|
||||
self.cx.current_expansion.depth = 0;
|
||||
|
||||
// Collect all macro invocations and replace them with placeholders.
|
||||
let (mut fragment_with_placeholders, mut invocations) =
|
||||
@ -488,6 +487,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||
};
|
||||
|
||||
let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data;
|
||||
let depth = depth - orig_expansion_data.depth;
|
||||
self.cx.current_expansion = invoc.expansion_data.clone();
|
||||
self.cx.force_mode = force;
|
||||
|
||||
@ -500,42 +500,16 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||
.resolver
|
||||
.take_derive_resolutions(expn_id)
|
||||
.map(|derives| {
|
||||
enum AnnotatableRef<'a> {
|
||||
Item(&'a P<ast::Item>),
|
||||
Stmt(&'a ast::Stmt),
|
||||
}
|
||||
let item = match &fragment {
|
||||
AstFragment::Items(items) => match &items[..] {
|
||||
[item] => AnnotatableRef::Item(item),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
AstFragment::Stmts(stmts) => match &stmts[..] {
|
||||
[stmt] => AnnotatableRef::Stmt(stmt),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
derive_invocations.reserve(derives.len());
|
||||
derives
|
||||
.into_iter()
|
||||
.map(|(path, _exts)| {
|
||||
.map(|(path, item, _exts)| {
|
||||
// FIXME: Consider using the derive resolutions (`_exts`)
|
||||
// instead of enqueuing the derives to be resolved again later.
|
||||
let expn_id = ExpnId::fresh(None);
|
||||
derive_invocations.push((
|
||||
Invocation {
|
||||
kind: InvocationKind::Derive {
|
||||
path,
|
||||
item: match item {
|
||||
AnnotatableRef::Item(item) => {
|
||||
Annotatable::Item(item.clone())
|
||||
}
|
||||
AnnotatableRef::Stmt(stmt) => {
|
||||
Annotatable::Stmt(P(stmt.clone()))
|
||||
}
|
||||
},
|
||||
},
|
||||
kind: InvocationKind::Derive { path, item },
|
||||
fragment_kind,
|
||||
expansion_data: ExpansionData {
|
||||
id: expn_id,
|
||||
|
@ -380,7 +380,7 @@ impl<'a> ResolverExpand for Resolver<'a> {
|
||||
has_derive_copy: false,
|
||||
});
|
||||
let parent_scope = self.invocation_parent_scopes[&expn_id];
|
||||
for (i, (path, opt_ext)) in entry.resolutions.iter_mut().enumerate() {
|
||||
for (i, (path, _, opt_ext)) in entry.resolutions.iter_mut().enumerate() {
|
||||
if opt_ext.is_none() {
|
||||
*opt_ext = Some(
|
||||
match self.resolve_macro_path(
|
||||
|
@ -813,6 +813,14 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
|
||||
match it.kind {
|
||||
hir::ItemKind::Fn(..) => tcx.ensure().fn_sig(def_id),
|
||||
hir::ItemKind::OpaqueTy(..) => tcx.ensure().item_bounds(def_id),
|
||||
hir::ItemKind::Const(ty, ..) | hir::ItemKind::Static(ty, ..) => {
|
||||
// (#75889): Account for `const C: dyn Fn() -> _ = "";`
|
||||
if let hir::TyKind::TraitObject(..) = ty.kind {
|
||||
let mut visitor = PlaceholderHirTyCollector::default();
|
||||
visitor.visit_item(it);
|
||||
placeholder_type_error(tcx, None, &[], visitor.0, false, None);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -27,12 +27,25 @@ use self::Ordering::*;
|
||||
/// Trait for equality comparisons which are [partial equivalence
|
||||
/// relations](https://en.wikipedia.org/wiki/Partial_equivalence_relation).
|
||||
///
|
||||
/// `x.eq(y)` can also be written `x == y`, and `x.ne(y)` can be written `x != y`.
|
||||
/// We use the easier-to-read infix notation in the remainder of this documentation.
|
||||
///
|
||||
/// This trait allows for partial equality, for types that do not have a full
|
||||
/// equivalence relation. For example, in floating point numbers `NaN != NaN`,
|
||||
/// so floating point types implement `PartialEq` but not [`trait@Eq`].
|
||||
///
|
||||
/// Formally, the equality must be (for all `a`, `b`, `c` of type `A`, `B`,
|
||||
/// `C`):
|
||||
/// Implementations must ensure that `eq` and `ne` are consistent with each other:
|
||||
///
|
||||
/// - `a != b` if and only if `!(a == b)`
|
||||
/// (ensured by the default implementation).
|
||||
///
|
||||
/// If [`PartialOrd`] or [`Ord`] are also implemented for `Self` and `Rhs`, their methods must also
|
||||
/// be consistent with `PartialEq` (see the documentation of those traits for the exact
|
||||
/// requirememts). It's easy to accidentally make them disagree by deriving some of the traits and
|
||||
/// manually implementing others.
|
||||
///
|
||||
/// The equality relation `==` must satisfy the following conditions
|
||||
/// (for all `a`, `b`, `c` of type `A`, `B`, `C`):
|
||||
///
|
||||
/// - **Symmetric**: if `A: PartialEq<B>` and `B: PartialEq<A>`, then **`a == b`
|
||||
/// implies `b == a`**; and
|
||||
@ -53,15 +66,6 @@ use self::Ordering::*;
|
||||
///
|
||||
/// ## How can I implement `PartialEq`?
|
||||
///
|
||||
/// `PartialEq` only requires the [`eq`] method to be implemented; [`ne`] is defined
|
||||
/// in terms of it by default. Any manual implementation of [`ne`] *must* respect
|
||||
/// the rule that [`eq`] is a strict inverse of [`ne`]; that is, `!(a == b)` if and
|
||||
/// only if `a != b`.
|
||||
///
|
||||
/// Implementations of `PartialEq`, [`PartialOrd`], and [`Ord`] *must* agree with
|
||||
/// each other. It's easy to accidentally make them disagree by deriving some
|
||||
/// of the traits and manually implementing others.
|
||||
///
|
||||
/// An example implementation for a domain in which two books are considered
|
||||
/// the same book if their ISBN matches, even if the formats differ:
|
||||
///
|
||||
@ -631,10 +635,25 @@ impl<T: Clone> Clone for Reverse<T> {
|
||||
|
||||
/// Trait for types that form a [total order](https://en.wikipedia.org/wiki/Total_order).
|
||||
///
|
||||
/// An order is a total order if it is (for all `a`, `b` and `c`):
|
||||
/// Implementations must be consistent with the [`PartialOrd`] implementation, and ensure
|
||||
/// `max`, `min`, and `clamp` are consistent with `cmp`:
|
||||
///
|
||||
/// - total and asymmetric: exactly one of `a < b`, `a == b` or `a > b` is true; and
|
||||
/// - transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
|
||||
/// - `partial_cmp(a, b) == Some(cmp(a, b))`.
|
||||
/// - `max(a, b) == max_by(a, b, cmp)` (ensured by the default implementation).
|
||||
/// - `min(a, b) == min_by(a, b, cmp)` (ensured by the default implementation).
|
||||
/// - For `a.clamp(min, max)`, see the [method docs](#method.clamp)
|
||||
/// (ensured by the default implementation).
|
||||
///
|
||||
/// It's easy to accidentally make `cmp` and `partial_cmp` disagree by
|
||||
/// deriving some of the traits and manually implementing others.
|
||||
///
|
||||
/// ## Corollaries
|
||||
///
|
||||
/// From the above and the requirements of `PartialOrd`, it follows that `<` defines a strict total order.
|
||||
/// This means that for all `a`, `b` and `c`:
|
||||
///
|
||||
/// - exactly one of `a < b`, `a == b` or `a > b` is true; and
|
||||
/// - `<` is transitive: `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
|
||||
///
|
||||
/// ## Derivable
|
||||
///
|
||||
@ -659,12 +678,6 @@ impl<T: Clone> Clone for Reverse<T> {
|
||||
/// Then you must define an implementation for [`cmp`]. You may find it useful to use
|
||||
/// [`cmp`] on your type's fields.
|
||||
///
|
||||
/// Implementations of [`PartialEq`], [`PartialOrd`], and `Ord` *must*
|
||||
/// agree with each other. That is, `a.cmp(b) == Ordering::Equal` if
|
||||
/// and only if `a == b` and `Some(a.cmp(b)) == a.partial_cmp(b)` for
|
||||
/// all `a` and `b`. It's easy to accidentally make them disagree by
|
||||
/// deriving some of the traits and manually implementing others.
|
||||
///
|
||||
/// Here's an example where you want to sort people by height only, disregarding `id`
|
||||
/// and `name`:
|
||||
///
|
||||
@ -824,15 +837,45 @@ impl PartialOrd for Ordering {
|
||||
|
||||
/// Trait for values that can be compared for a sort-order.
|
||||
///
|
||||
/// The `lt`, `le`, `gt`, and `ge` methods of this trait can be called using
|
||||
/// the `<`, `<=`, `>`, and `>=` operators, respectively.
|
||||
///
|
||||
/// The methods of this trait must be consistent with each other and with those of `PartialEq` in
|
||||
/// the following sense:
|
||||
///
|
||||
/// - `a == b` if and only if `partial_cmp(a, b) == Some(Equal)`.
|
||||
/// - `a < b` if and only if `partial_cmp(a, b) == Some(Less)`
|
||||
/// (ensured by the default implementation).
|
||||
/// - `a > b` if and only if `partial_cmp(a, b) == Some(Greater)`
|
||||
/// (ensured by the default implementation).
|
||||
/// - `a <= b` if and only if `a < b || a == b`
|
||||
/// (ensured by the default implementation).
|
||||
/// - `a >= b` if and only if `a > b || a == b`
|
||||
/// (ensured by the default implementation).
|
||||
/// - `a != b` if and only if `!(a == b)` (already part of `PartialEq`).
|
||||
///
|
||||
/// If [`Ord`] is also implemented for `Self` and `Rhs`, it must also be consistent with
|
||||
/// `partial_cmp` (see the documentation of that trait for the exact requirements). It's
|
||||
/// easy to accidentally make them disagree by deriving some of the traits and manually
|
||||
/// implementing others.
|
||||
///
|
||||
/// The comparison must satisfy, for all `a`, `b` and `c`:
|
||||
///
|
||||
/// - asymmetry: if `a < b` then `!(a > b)`, as well as `a > b` implying `!(a < b)`; and
|
||||
/// - transitivity: `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
|
||||
/// - duality: `a < b` if and only if `b > a`.
|
||||
///
|
||||
/// Note that these requirements mean that the trait itself must be implemented symmetrically and
|
||||
/// transitively: if `T: PartialOrd<U>` and `U: PartialOrd<V>` then `U: PartialOrd<T>` and `T:
|
||||
/// PartialOrd<V>`.
|
||||
///
|
||||
/// ## Corollaries
|
||||
///
|
||||
/// The following corollaries follow from the above requirements:
|
||||
///
|
||||
/// - irreflexivity of `<` and `>`: `!(a < a)`, `!(a > a)`
|
||||
/// - transitivity of `>`: if `a > b` and `b > c` then `a > c`
|
||||
/// - duality of `partial_cmp`: `partial_cmp(a, b) == partial_cmp(b, a).map(Ordering::reverse)`
|
||||
///
|
||||
/// ## Derivable
|
||||
///
|
||||
/// This trait can be used with `#[derive]`. When `derive`d on structs, it will produce a
|
||||
@ -850,10 +893,6 @@ impl PartialOrd for Ordering {
|
||||
///
|
||||
/// `PartialOrd` requires your type to be [`PartialEq`].
|
||||
///
|
||||
/// Implementations of [`PartialEq`], `PartialOrd`, and [`Ord`] *must* agree with each other. It's
|
||||
/// easy to accidentally make them disagree by deriving some of the traits and manually
|
||||
/// implementing others.
|
||||
///
|
||||
/// If your type is [`Ord`], you can implement [`partial_cmp`] by using [`cmp`]:
|
||||
///
|
||||
/// ```
|
||||
|
@ -295,9 +295,10 @@ where
|
||||
let sz_a = self.a.size();
|
||||
if A::MAY_HAVE_SIDE_EFFECT && sz_a > self.len {
|
||||
for _ in 0..sz_a - self.len {
|
||||
self.a_len -= 1;
|
||||
self.a.next_back();
|
||||
}
|
||||
self.a_len = self.len;
|
||||
debug_assert_eq!(self.a_len, self.len);
|
||||
}
|
||||
let sz_b = self.b.size();
|
||||
if B::MAY_HAVE_SIDE_EFFECT && sz_b > self.len {
|
||||
|
@ -232,6 +232,33 @@ fn test_zip_trusted_random_access_composition() {
|
||||
assert_eq!(z2.next().unwrap(), ((1, 1), 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(panic = "unwind")]
|
||||
fn test_zip_trusted_random_access_next_back_drop() {
|
||||
use std::panic::catch_unwind;
|
||||
use std::panic::AssertUnwindSafe;
|
||||
|
||||
let mut counter = 0;
|
||||
|
||||
let it = [42].iter().map(|e| {
|
||||
let c = counter;
|
||||
counter += 1;
|
||||
if c == 0 {
|
||||
panic!("bomb");
|
||||
}
|
||||
|
||||
e
|
||||
});
|
||||
let it2 = [(); 0].iter();
|
||||
let mut zip = it.zip(it2);
|
||||
catch_unwind(AssertUnwindSafe(|| {
|
||||
zip.next_back();
|
||||
}))
|
||||
.unwrap_err();
|
||||
assert!(zip.next().is_none());
|
||||
assert_eq!(counter, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_double_ended_zip() {
|
||||
let xs = [1, 2, 3, 4, 5, 6];
|
||||
|
@ -50,6 +50,7 @@ time = "0.1"
|
||||
ignore = "0.4.10"
|
||||
opener = "0.4"
|
||||
merge = "0.1.0"
|
||||
once_cell = "1.7.2"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.winapi]
|
||||
version = "0.3"
|
||||
|
@ -29,6 +29,8 @@ use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir};
|
||||
use crate::{Build, DocTests, GitRepo, Mode};
|
||||
|
||||
pub use crate::Compiler;
|
||||
// FIXME: replace with std::lazy after it gets stabilized and reaches beta
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
pub struct Builder<'a> {
|
||||
pub build: &'a Build,
|
||||
@ -195,7 +197,7 @@ impl StepDescription {
|
||||
|
||||
if paths.is_empty() || builder.config.include_default_paths {
|
||||
for (desc, should_run) in v.iter().zip(&should_runs) {
|
||||
if desc.default && should_run.is_really_default {
|
||||
if desc.default && should_run.is_really_default() {
|
||||
for pathset in &should_run.paths {
|
||||
desc.maybe_run(builder, pathset);
|
||||
}
|
||||
@ -228,7 +230,11 @@ impl StepDescription {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum ReallyDefault<'a> {
|
||||
Bool(bool),
|
||||
Lazy(Lazy<bool, Box<dyn Fn() -> bool + 'a>>),
|
||||
}
|
||||
|
||||
pub struct ShouldRun<'a> {
|
||||
pub builder: &'a Builder<'a>,
|
||||
// use a BTreeSet to maintain sort order
|
||||
@ -236,7 +242,7 @@ pub struct ShouldRun<'a> {
|
||||
|
||||
// If this is a default rule, this is an additional constraint placed on
|
||||
// its run. Generally something like compiler docs being enabled.
|
||||
is_really_default: bool,
|
||||
is_really_default: ReallyDefault<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ShouldRun<'a> {
|
||||
@ -244,15 +250,27 @@ impl<'a> ShouldRun<'a> {
|
||||
ShouldRun {
|
||||
builder,
|
||||
paths: BTreeSet::new(),
|
||||
is_really_default: true, // by default no additional conditions
|
||||
is_really_default: ReallyDefault::Bool(true), // by default no additional conditions
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_condition(mut self, cond: bool) -> Self {
|
||||
self.is_really_default = cond;
|
||||
self.is_really_default = ReallyDefault::Bool(cond);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn lazy_default_condition(mut self, lazy_cond: Box<dyn Fn() -> bool + 'a>) -> Self {
|
||||
self.is_really_default = ReallyDefault::Lazy(Lazy::new(lazy_cond));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_really_default(&self) -> bool {
|
||||
match &self.is_really_default {
|
||||
ReallyDefault::Bool(val) => *val,
|
||||
ReallyDefault::Lazy(lazy) => *lazy.deref(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates it should run if the command-line selects the given crate or
|
||||
/// any of its (local) dependencies.
|
||||
///
|
||||
|
@ -613,9 +613,14 @@ mod dist {
|
||||
// Note that the stages here are +1 than what they actually are because
|
||||
// Rustdoc::run swaps out the compiler with stage minus 1 if --stage is
|
||||
// not 0.
|
||||
//
|
||||
// The stage 0 copy is the one downloaded for bootstrapping. It is
|
||||
// (currently) needed to run "cargo test" on the linkchecker, and
|
||||
// should be relatively "free".
|
||||
assert_eq!(
|
||||
first(builder.cache.all::<tool::Rustdoc>()),
|
||||
&[
|
||||
tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } },
|
||||
tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } },
|
||||
tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },
|
||||
]
|
||||
|
@ -124,8 +124,25 @@ You can skip linkcheck with --exclude src/tools/linkchecker"
|
||||
|
||||
builder.info(&format!("Linkcheck ({})", host));
|
||||
|
||||
// Test the linkchecker itself.
|
||||
let bootstrap_host = builder.config.build;
|
||||
let compiler = builder.compiler(0, bootstrap_host);
|
||||
let cargo = tool::prepare_tool_cargo(
|
||||
builder,
|
||||
compiler,
|
||||
Mode::ToolBootstrap,
|
||||
bootstrap_host,
|
||||
"test",
|
||||
"src/tools/linkchecker",
|
||||
SourceType::InTree,
|
||||
&[],
|
||||
);
|
||||
try_run(builder, &mut cargo.into());
|
||||
|
||||
// Build all the default documentation.
|
||||
builder.default_doc(&[]);
|
||||
|
||||
// Run the linkchecker.
|
||||
let _time = util::timeit(&builder);
|
||||
try_run(
|
||||
builder,
|
||||
@ -806,15 +823,15 @@ impl Step for RustdocGUI {
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
let builder = run.builder;
|
||||
let run = run.suite_path("src/test/rustdoc-gui");
|
||||
run.default_condition(
|
||||
run.lazy_default_condition(Box::new(move || {
|
||||
builder.config.nodejs.is_some()
|
||||
&& builder
|
||||
.config
|
||||
.npm
|
||||
.as_ref()
|
||||
.map(|p| check_if_browser_ui_test_is_installed(p))
|
||||
.unwrap_or(false),
|
||||
)
|
||||
.unwrap_or(false)
|
||||
}))
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
|
@ -2,5 +2,4 @@ fn foo() -> _ { 5 } //~ ERROR E0121
|
||||
|
||||
static BAR: _ = "test"; //~ ERROR E0121
|
||||
|
||||
fn main() {
|
||||
}
|
||||
fn main() {}
|
||||
|
@ -0,0 +1,16 @@
|
||||
// Regression test for #84632: Recursion limit is ignored
|
||||
// for builtin macros that eagerly expands.
|
||||
|
||||
#![recursion_limit = "15"]
|
||||
macro_rules! a {
|
||||
() => ("");
|
||||
(A) => (concat!("", a!()));
|
||||
(A, $($A:ident),*) => (concat!("", a!($($A),*)))
|
||||
//~^ ERROR recursion limit reached
|
||||
//~| HELP consider adding
|
||||
}
|
||||
|
||||
fn main() {
|
||||
a!(A, A, A, A, A);
|
||||
a!(A, A, A, A, A, A, A, A, A, A, A);
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
error: recursion limit reached while expanding `concat!`
|
||||
--> $DIR/issue-84632-eager-expansion-recursion-limit.rs:8:28
|
||||
|
|
||||
LL | (A, $($A:ident),*) => (concat!("", a!($($A),*)))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | a!(A, A, A, A, A, A, A, A, A, A, A);
|
||||
| ------------------------------------ in this macro invocation
|
||||
|
|
||||
= help: consider adding a `#![recursion_limit="30"]` attribute to your crate (`issue_84632_eager_expansion_recursion_limit`)
|
||||
= note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to previous error
|
||||
|
6
src/test/ui/typeck/issue-75889.rs
Normal file
6
src/test/ui/typeck/issue-75889.rs
Normal file
@ -0,0 +1,6 @@
|
||||
// Regression test for #75889.
|
||||
|
||||
const FOO: dyn Fn() -> _ = ""; //~ ERROR E0121
|
||||
static BOO: dyn Fn() -> _ = ""; //~ ERROR E0121
|
||||
|
||||
fn main() {}
|
15
src/test/ui/typeck/issue-75889.stderr
Normal file
15
src/test/ui/typeck/issue-75889.stderr
Normal file
@ -0,0 +1,15 @@
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/issue-75889.rs:3:24
|
||||
|
|
||||
LL | const FOO: dyn Fn() -> _ = "";
|
||||
| ^ not allowed in type signatures
|
||||
|
||||
error[E0121]: the type placeholder `_` is not allowed within types on item signatures
|
||||
--> $DIR/issue-75889.rs:4:25
|
||||
|
|
||||
LL | static BOO: dyn Fn() -> _ = "";
|
||||
| ^ not allowed in type signatures
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0121`.
|
@ -347,7 +347,7 @@ impl Checker {
|
||||
} else {
|
||||
report.errors += 1;
|
||||
print!("{}:{}: broken link fragment ", pretty_path, i + 1);
|
||||
println!("`#{}` pointing to `{}`", fragment, pretty_path);
|
||||
println!("`#{}` pointing to `{}`", fragment, target_pretty_path);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;URL=sometarget">
|
||||
<title>Redirection</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Redirecting to <a href="sometarget">sometarget</a>...</p>
|
||||
|
@ -15,7 +15,7 @@ fn run(dirname: &str) -> (ExitStatus, String, String) {
|
||||
fn broken_test(dirname: &str, expected: &str) {
|
||||
let (status, stdout, stderr) = run(dirname);
|
||||
assert!(!status.success());
|
||||
if !stdout.contains(expected) {
|
||||
if !contains(expected, &stdout) {
|
||||
panic!(
|
||||
"stdout did not contain expected text: {}\n\
|
||||
--- stdout:\n\
|
||||
@ -27,6 +27,25 @@ fn broken_test(dirname: &str, expected: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(expected: &str, actual: &str) -> bool {
|
||||
// Normalize for Windows paths.
|
||||
let actual = actual.replace('\\', "/");
|
||||
actual.lines().any(|mut line| {
|
||||
for (i, part) in expected.split("[..]").enumerate() {
|
||||
match line.find(part) {
|
||||
Some(j) => {
|
||||
if i == 0 && j != 0 {
|
||||
return false;
|
||||
}
|
||||
line = &line[j + part.len()..];
|
||||
}
|
||||
None => return false,
|
||||
}
|
||||
}
|
||||
line.is_empty() || expected.ends_with("[..]")
|
||||
})
|
||||
}
|
||||
|
||||
fn valid_test(dirname: &str) {
|
||||
let (status, stdout, stderr) = run(dirname);
|
||||
if !status.success() {
|
||||
@ -48,30 +67,47 @@ fn valid() {
|
||||
|
||||
#[test]
|
||||
fn basic_broken() {
|
||||
broken_test("basic_broken", "bar.html");
|
||||
broken_test("basic_broken", "foo.html:3: broken link - `bar.html`");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn broken_fragment_local() {
|
||||
broken_test("broken_fragment_local", "#somefrag");
|
||||
broken_test(
|
||||
"broken_fragment_local",
|
||||
"foo.html:3: broken link fragment `#somefrag` pointing to `foo.html`",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn broken_fragment_remote() {
|
||||
broken_test("broken_fragment_remote/inner", "#somefrag");
|
||||
broken_test(
|
||||
"broken_fragment_remote/inner",
|
||||
"foo.html:3: broken link fragment `#somefrag` pointing to \
|
||||
`[..]/broken_fragment_remote/bar.html`",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn broken_redir() {
|
||||
broken_test("broken_redir", "sometarget");
|
||||
broken_test(
|
||||
"broken_redir",
|
||||
"foo.html:3: broken redirect from `redir-bad.html` to `sometarget`",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn directory_link() {
|
||||
broken_test("directory_link", "somedir");
|
||||
broken_test(
|
||||
"directory_link",
|
||||
"foo.html:3: directory link to `somedir` (directory links should use index.html instead)",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redirect_loop() {
|
||||
broken_test("redirect_loop", "redir-bad.html");
|
||||
broken_test(
|
||||
"redirect_loop",
|
||||
"foo.html:3: redirect from `redir-bad.html` to `[..]redirect_loop/redir-bad.html` \
|
||||
which is also a redirect (not supported)",
|
||||
);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;URL=redir-bad.html">
|
||||
<title>Redirection</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Redirecting to <a href="redir-bad.html">redir-bad.html</a>...</p>
|
||||
|
@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;URL=xxx">
|
||||
<title>Redirection</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Redirecting to <a href="xxx">xxx</a>...</p>
|
||||
|
@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;URL=redir-target.html">
|
||||
<title>Redirection</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Redirecting to <a href="redir-target.html">redir-target.html</a>...</p>
|
||||
|
Loading…
x
Reference in New Issue
Block a user