Auto merge of #109581 - matthiaskrgr:rollup-e8fi2vi, r=matthiaskrgr

Rollup of 7 pull requests

Successful merges:

 - #109355 (Fix bad suggestion for clone/is_some in field init shorthand)
 - #109484 (Bugfix: avoid panic on invalid json output from libtest)
 - #109539 (Refactor `find_*_stability` functions)
 - #109542 (rustdoc: clean up `storage.js`)
 - #109545 (Deeply check well-formedness of return-position `impl Trait` in trait)
 - #109568 (miri: fix raw pointer dyn receivers)
 - #109570 (Add GUI test for "Auto-hide item methods' documentation" setting)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-03-25 14:34:13 +00:00
commit 96bd50dd47
18 changed files with 633 additions and 448 deletions

View File

@ -226,315 +226,39 @@ pub fn to_opt_reason(&self) -> Option<Symbol> {
} }
} }
/// Collects stability info from all stability attributes in `attrs`. /// Collects stability info from `stable`/`unstable`/`rustc_allowed_through_unstable_modules`
/// Returns `None` if no stability attributes are found. /// attributes in `attrs`. Returns `None` if no stability attributes are found.
pub fn find_stability( pub fn find_stability(
sess: &Session, sess: &Session,
attrs: &[Attribute], attrs: &[Attribute],
item_sp: Span, item_sp: Span,
) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>, Option<(DefaultBodyStability, Span)>) ) -> Option<(Stability, Span)> {
{
find_stability_generic(sess, attrs.iter(), item_sp)
}
fn find_stability_generic<'a, I>(
sess: &Session,
attrs_iter: I,
item_sp: Span,
) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>, Option<(DefaultBodyStability, Span)>)
where
I: Iterator<Item = &'a Attribute>,
{
use StabilityLevel::*;
let mut stab: Option<(Stability, Span)> = None; let mut stab: Option<(Stability, Span)> = None;
let mut const_stab: Option<(ConstStability, Span)> = None;
let mut body_stab: Option<(DefaultBodyStability, Span)> = None;
let mut promotable = false;
let mut allowed_through_unstable_modules = false; let mut allowed_through_unstable_modules = false;
'outer: for attr in attrs_iter { for attr in attrs {
if ![ match attr.name_or_empty() {
sym::rustc_const_unstable, sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true,
sym::rustc_const_stable, sym::unstable => {
sym::unstable, if stab.is_some() {
sym::stable, handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels);
sym::rustc_promotable, break;
sym::rustc_allowed_through_unstable_modules,
sym::rustc_default_body_unstable,
]
.iter()
.any(|&s| attr.has_name(s))
{
continue; // not a stability level
}
let meta = attr.meta();
if attr.has_name(sym::rustc_promotable) {
promotable = true;
} else if attr.has_name(sym::rustc_allowed_through_unstable_modules) {
allowed_through_unstable_modules = true;
}
// attributes with data
else if let Some(meta @ MetaItem { kind: MetaItemKind::List(metas), .. }) = &meta {
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
if item.is_some() {
handle_errors(
&sess.parse_sess,
meta.span,
AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
);
return false;
} }
if let Some(v) = meta.value_str() {
*item = Some(v); if let Some((feature, level)) = parse_unstability(sess, attr) {
true stab = Some((Stability { level, feature }, attr.span));
} else {
sess.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
false
} }
};
let meta_name = meta.name_or_empty();
match meta_name {
sym::rustc_const_unstable | sym::rustc_default_body_unstable | sym::unstable => {
if meta_name == sym::unstable && stab.is_some() {
handle_errors(
&sess.parse_sess,
attr.span,
AttrError::MultipleStabilityLevels,
);
break;
} else if meta_name == sym::rustc_const_unstable && const_stab.is_some() {
handle_errors(
&sess.parse_sess,
attr.span,
AttrError::MultipleStabilityLevels,
);
break;
} else if meta_name == sym::rustc_default_body_unstable && body_stab.is_some() {
handle_errors(
&sess.parse_sess,
attr.span,
AttrError::MultipleStabilityLevels,
);
break;
}
let mut feature = None;
let mut reason = None;
let mut issue = None;
let mut issue_num = None;
let mut is_soft = false;
let mut implied_by = None;
for meta in metas {
let Some(mi) = meta.meta_item() else {
handle_errors(
&sess.parse_sess,
meta.span(),
AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false),
);
continue 'outer;
};
match mi.name_or_empty() {
sym::feature => {
if !get(mi, &mut feature) {
continue 'outer;
}
}
sym::reason => {
if !get(mi, &mut reason) {
continue 'outer;
}
}
sym::issue => {
if !get(mi, &mut issue) {
continue 'outer;
}
// These unwraps are safe because `get` ensures the meta item
// is a name/value pair string literal.
issue_num = match issue.unwrap().as_str() {
"none" => None,
issue => match issue.parse::<NonZeroU32>() {
Ok(num) => Some(num),
Err(err) => {
sess.emit_err(
session_diagnostics::InvalidIssueString {
span: mi.span,
cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
mi.name_value_literal_span().unwrap(),
err.kind(),
),
},
);
continue 'outer;
}
},
};
}
sym::soft => {
if !mi.is_word() {
sess.emit_err(session_diagnostics::SoftNoArgs {
span: mi.span,
});
}
is_soft = true;
}
sym::implied_by => {
if !get(mi, &mut implied_by) {
continue 'outer;
}
}
_ => {
handle_errors(
&sess.parse_sess,
meta.span(),
AttrError::UnknownMetaItem(
pprust::path_to_string(&mi.path),
&["feature", "reason", "issue", "soft"],
),
);
continue 'outer;
}
}
}
match (feature, reason, issue) {
(Some(feature), reason, Some(_)) => {
if !rustc_lexer::is_ident(feature.as_str()) {
handle_errors(
&sess.parse_sess,
attr.span,
AttrError::NonIdentFeature,
);
continue;
}
let level = Unstable {
reason: UnstableReason::from_opt_reason(reason),
issue: issue_num,
is_soft,
implied_by,
};
if sym::unstable == meta_name {
stab = Some((Stability { level, feature }, attr.span));
} else if sym::rustc_const_unstable == meta_name {
const_stab = Some((
ConstStability { level, feature, promotable: false },
attr.span,
));
} else if sym::rustc_default_body_unstable == meta_name {
body_stab =
Some((DefaultBodyStability { level, feature }, attr.span));
} else {
unreachable!("Unknown stability attribute {meta_name}");
}
}
(None, _, _) => {
handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
continue;
}
_ => {
sess.emit_err(session_diagnostics::MissingIssue { span: attr.span });
continue;
}
}
}
sym::rustc_const_stable | sym::stable => {
if meta_name == sym::stable && stab.is_some() {
handle_errors(
&sess.parse_sess,
attr.span,
AttrError::MultipleStabilityLevels,
);
break;
} else if meta_name == sym::rustc_const_stable && const_stab.is_some() {
handle_errors(
&sess.parse_sess,
attr.span,
AttrError::MultipleStabilityLevels,
);
break;
}
let mut feature = None;
let mut since = None;
for meta in metas {
match meta {
NestedMetaItem::MetaItem(mi) => match mi.name_or_empty() {
sym::feature => {
if !get(mi, &mut feature) {
continue 'outer;
}
}
sym::since => {
if !get(mi, &mut since) {
continue 'outer;
}
}
_ => {
handle_errors(
&sess.parse_sess,
meta.span(),
AttrError::UnknownMetaItem(
pprust::path_to_string(&mi.path),
&["feature", "since"],
),
);
continue 'outer;
}
},
NestedMetaItem::Lit(lit) => {
handle_errors(
&sess.parse_sess,
lit.span,
AttrError::UnsupportedLiteral(
UnsupportedLiteralReason::Generic,
false,
),
);
continue 'outer;
}
}
}
if let Some(s) = since && s.as_str() == VERSION_PLACEHOLDER {
since = Some(rust_version_symbol());
}
match (feature, since) {
(Some(feature), Some(since)) => {
let level = Stable { since, allowed_through_unstable_modules: false };
if sym::stable == meta_name {
stab = Some((Stability { level, feature }, attr.span));
} else {
const_stab = Some((
ConstStability { level, feature, promotable: false },
attr.span,
));
}
}
(None, _) => {
handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
continue;
}
_ => {
handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
continue;
}
}
}
_ => unreachable!(),
} }
} sym::stable => {
} if stab.is_some() {
handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels);
// Merge the const-unstable info into the stability info break;
if promotable { }
match &mut const_stab { if let Some((feature, level)) = parse_stability(sess, attr) {
Some((stab, _)) => stab.promotable = promotable, stab = Some((Stability { level, feature }, attr.span));
_ => _ = sess.emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp }), }
}
_ => {}
} }
} }
@ -553,7 +277,286 @@ fn find_stability_generic<'a, I>(
} }
} }
(stab, const_stab, body_stab) stab
}
/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable`
/// attributes in `attrs`. Returns `None` if no stability attributes are found.
pub fn find_const_stability(
sess: &Session,
attrs: &[Attribute],
item_sp: Span,
) -> Option<(ConstStability, Span)> {
let mut const_stab: Option<(ConstStability, Span)> = None;
let mut promotable = false;
for attr in attrs {
match attr.name_or_empty() {
sym::rustc_promotable => promotable = true,
sym::rustc_const_unstable => {
if const_stab.is_some() {
handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels);
break;
}
if let Some((feature, level)) = parse_unstability(sess, attr) {
const_stab =
Some((ConstStability { level, feature, promotable: false }, attr.span));
}
}
sym::rustc_const_stable => {
if const_stab.is_some() {
handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels);
break;
}
if let Some((feature, level)) = parse_stability(sess, attr) {
const_stab =
Some((ConstStability { level, feature, promotable: false }, attr.span));
}
}
_ => {}
}
}
// Merge the const-unstable info into the stability info
if promotable {
match &mut const_stab {
Some((stab, _)) => stab.promotable = promotable,
_ => _ = sess.emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp }),
}
}
const_stab
}
/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`.
/// Returns `None` if no stability attributes are found.
pub fn find_body_stability(
sess: &Session,
attrs: &[Attribute],
) -> Option<(DefaultBodyStability, Span)> {
let mut body_stab: Option<(DefaultBodyStability, Span)> = None;
for attr in attrs {
if attr.has_name(sym::rustc_default_body_unstable) {
if body_stab.is_some() {
handle_errors(&sess.parse_sess, attr.span, AttrError::MultipleStabilityLevels);
break;
}
if let Some((feature, level)) = parse_unstability(sess, attr) {
body_stab = Some((DefaultBodyStability { level, feature }, attr.span));
}
}
}
body_stab
}
/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
/// its stability information.
fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, StabilityLevel)> {
let meta = attr.meta()?;
let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None };
let insert_or_error = |meta: &MetaItem, item: &mut Option<Symbol>| {
if item.is_some() {
handle_errors(
&sess.parse_sess,
meta.span,
AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
);
return false;
}
if let Some(v) = meta.value_str() {
*item = Some(v);
true
} else {
sess.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
false
}
};
let mut feature = None;
let mut since = None;
for meta in metas {
let Some(mi) = meta.meta_item() else {
handle_errors(
&sess.parse_sess,
meta.span(),
AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false),
);
return None;
};
match mi.name_or_empty() {
sym::feature => {
if !insert_or_error(mi, &mut feature) {
return None;
}
}
sym::since => {
if !insert_or_error(mi, &mut since) {
return None;
}
}
_ => {
handle_errors(
&sess.parse_sess,
meta.span(),
AttrError::UnknownMetaItem(
pprust::path_to_string(&mi.path),
&["feature", "since"],
),
);
return None;
}
}
}
if let Some(s) = since && s.as_str() == VERSION_PLACEHOLDER {
since = Some(rust_version_symbol());
}
match (feature, since) {
(Some(feature), Some(since)) => {
let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false };
Some((feature, level))
}
(None, _) => {
handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
None
}
_ => {
handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
None
}
}
}
/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
/// attribute, and return the feature name and its stability information.
fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, StabilityLevel)> {
let meta = attr.meta()?;
let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None };
let insert_or_error = |meta: &MetaItem, item: &mut Option<Symbol>| {
if item.is_some() {
handle_errors(
&sess.parse_sess,
meta.span,
AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
);
return false;
}
if let Some(v) = meta.value_str() {
*item = Some(v);
true
} else {
sess.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
false
}
};
let mut feature = None;
let mut reason = None;
let mut issue = None;
let mut issue_num = None;
let mut is_soft = false;
let mut implied_by = None;
for meta in metas {
let Some(mi) = meta.meta_item() else {
handle_errors(
&sess.parse_sess,
meta.span(),
AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false),
);
return None;
};
match mi.name_or_empty() {
sym::feature => {
if !insert_or_error(mi, &mut feature) {
return None;
}
}
sym::reason => {
if !insert_or_error(mi, &mut reason) {
return None;
}
}
sym::issue => {
if !insert_or_error(mi, &mut issue) {
return None;
}
// These unwraps are safe because `insert_or_error` ensures the meta item
// is a name/value pair string literal.
issue_num = match issue.unwrap().as_str() {
"none" => None,
issue => match issue.parse::<NonZeroU32>() {
Ok(num) => Some(num),
Err(err) => {
sess.emit_err(
session_diagnostics::InvalidIssueString {
span: mi.span,
cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
mi.name_value_literal_span().unwrap(),
err.kind(),
),
},
);
return None;
}
},
};
}
sym::soft => {
if !mi.is_word() {
sess.emit_err(session_diagnostics::SoftNoArgs { span: mi.span });
}
is_soft = true;
}
sym::implied_by => {
if !insert_or_error(mi, &mut implied_by) {
return None;
}
}
_ => {
handle_errors(
&sess.parse_sess,
meta.span(),
AttrError::UnknownMetaItem(
pprust::path_to_string(&mi.path),
&["feature", "reason", "issue", "soft", "implied_by"],
),
);
return None;
}
}
}
match (feature, reason, issue) {
(Some(feature), reason, Some(_)) => {
if !rustc_lexer::is_ident(feature.as_str()) {
handle_errors(&sess.parse_sess, attr.span, AttrError::NonIdentFeature);
return None;
}
let level = StabilityLevel::Unstable {
reason: UnstableReason::from_opt_reason(reason),
issue: issue_num,
is_soft,
implied_by,
};
Some((feature, level))
}
(None, _, _) => {
handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
return None;
}
_ => {
sess.emit_err(session_diagnostics::MissingIssue { span: attr.span });
return None;
}
}
} }
pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> { pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {

View File

@ -539,7 +539,15 @@ pub(crate) fn eval_fn_call(
let mut receiver = args[0].clone(); let mut receiver = args[0].clone();
let receiver_place = loop { let receiver_place = loop {
match receiver.layout.ty.kind() { match receiver.layout.ty.kind() {
ty::Ref(..) | ty::RawPtr(..) => break self.deref_operand(&receiver)?, ty::Ref(..) | ty::RawPtr(..) => {
// We do *not* use `deref_operand` here: we don't want to conceptually
// create a place that must be dereferenceable, since the receiver might
// be a raw pointer and (for `*const dyn Trait`) we don't need to
// actually access memory to resolve this method.
// Also see <https://github.com/rust-lang/miri/issues/2786>.
let val = self.read_immediate(&receiver)?;
break self.ref_to_mplace(&val)?;
}
ty::Dynamic(.., ty::Dyn) => break receiver.assert_mem_place(), // no immediate unsized values ty::Dynamic(.., ty::Dyn) => break receiver.assert_mem_place(), // no immediate unsized values
ty::Dynamic(.., ty::DynStar) => { ty::Dynamic(.., ty::DynStar) => {
// Not clear how to handle this, so far we assume the receiver is always a pointer. // Not clear how to handle this, so far we assume the receiver is always a pointer.

View File

@ -793,7 +793,9 @@ pub fn new(
) )
}) })
.unwrap_or_else(|| (None, helper_attrs)); .unwrap_or_else(|| (None, helper_attrs));
let (stability, const_stability, body_stability) = attr::find_stability(&sess, attrs, span); let stability = attr::find_stability(&sess, attrs, span);
let const_stability = attr::find_const_stability(&sess, attrs, span);
let body_stability = attr::find_body_stability(&sess, attrs);
if let Some((_, sp)) = const_stability { if let Some((_, sp)) = const_stability {
sess.emit_err(errors::MacroConstStability { sess.emit_err(errors::MacroConstStability {
span: sp, span: sp,

View File

@ -1544,42 +1544,81 @@ fn check_return_position_impl_trait_in_trait_bounds<'tcx>(
span: Span, span: Span,
) { ) {
let tcx = wfcx.tcx(); let tcx = wfcx.tcx();
if let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id()) let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id()) else {
&& assoc_item.container == ty::AssocItemContainer::TraitContainer return;
{ };
// FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): Even with the new lowering if assoc_item.container != ty::AssocItemContainer::TraitContainer {
// strategy, we can't just call `check_associated_item` on the new RPITITs, return;
// because tests like `tests/ui/async-await/in-trait/implied-bounds.rs` will fail. }
// That's because we need to check that the bounds of the RPITIT hold using fn_output.visit_with(&mut ImplTraitInTraitFinder {
// the special substs that we create during opaque type lowering, otherwise we're wfcx,
// getting a bunch of early bound and free regions mixed up... Haven't looked too fn_def_id,
// deep into this, though. depth: ty::INNERMOST,
for arg in fn_output.walk() { seen: FxHashSet::default(),
if let ty::GenericArgKind::Type(ty) = arg.unpack() });
// RPITITs are always eagerly normalized into opaques, so always look for an }
// opaque here.
&& let ty::Alias(ty::Opaque, opaque_ty) = ty.kind() // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): Even with the new lowering
&& let Some(opaque_def_id) = opaque_ty.def_id.as_local() // strategy, we can't just call `check_associated_item` on the new RPITITs,
&& let opaque = tcx.hir().expect_item(opaque_def_id).expect_opaque_ty() // because tests like `tests/ui/async-await/in-trait/implied-bounds.rs` will fail.
&& let hir::OpaqueTyOrigin::FnReturn(source) | hir::OpaqueTyOrigin::AsyncFn(source) = opaque.origin // That's because we need to check that the bounds of the RPITIT hold using
&& source == fn_def_id // the special substs that we create during opaque type lowering, otherwise we're
// getting a bunch of early bound and free regions mixed up... Haven't looked too
// deep into this, though.
struct ImplTraitInTraitFinder<'a, 'tcx> {
wfcx: &'a WfCheckingCtxt<'a, 'tcx>,
fn_def_id: LocalDefId,
depth: ty::DebruijnIndex,
seen: FxHashSet<DefId>,
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
type BreakTy = !;
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<!> {
let tcx = self.wfcx.tcx();
if let ty::Alias(ty::Opaque, unshifted_opaque_ty) = *ty.kind()
&& self.seen.insert(unshifted_opaque_ty.def_id)
&& let Some(opaque_def_id) = unshifted_opaque_ty.def_id.as_local()
&& let opaque = tcx.hir().expect_item(opaque_def_id).expect_opaque_ty()
&& let hir::OpaqueTyOrigin::FnReturn(source) | hir::OpaqueTyOrigin::AsyncFn(source) = opaque.origin
&& source == self.fn_def_id
{
let opaque_ty = tcx.fold_regions(unshifted_opaque_ty, |re, depth| {
if let ty::ReLateBound(index, bv) = re.kind() {
if depth != ty::INNERMOST {
return tcx.mk_re_error_with_message(
DUMMY_SP,
"we shouldn't walk non-predicate binders with `impl Trait`...",
);
}
tcx.mk_re_late_bound(index.shifted_out_to_binder(self.depth), bv)
} else {
re
}
});
for (bound, bound_span) in tcx
.bound_explicit_item_bounds(opaque_ty.def_id)
.subst_iter_copied(tcx, opaque_ty.substs)
{ {
let span = tcx.def_span(opaque_ty.def_id); let bound = self.wfcx.normalize(bound_span, None, bound);
let bounds = wfcx.tcx().explicit_item_bounds(opaque_ty.def_id); self.wfcx.register_obligations(traits::wf::predicate_obligations(
let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| { self.wfcx.infcx,
let bound = ty::EarlyBinder(bound).subst(tcx, opaque_ty.substs); self.wfcx.param_env,
let normalized_bound = wfcx.normalize(span, None, bound); self.wfcx.body_def_id,
traits::wf::predicate_obligations( bound,
wfcx.infcx, bound_span,
wfcx.param_env, ));
wfcx.body_def_id, // Set the debruijn index back to innermost here, since we already eagerly
normalized_bound, // shifted the substs that we use to generate these bounds. This is unfortunately
bound_span, // subtly different behavior than the `ImplTraitInTraitFinder` we use in `param_env`,
) // but that function doesn't actually need to normalize the bound it's visiting
}); // (whereas we have to do so here)...
wfcx.register_obligations(wf_obligations); let old_depth = std::mem::replace(&mut self.depth, ty::INNERMOST);
bound.visit_with(self);
self.depth = old_depth;
} }
} }
ty.super_visit_with(self)
} }
} }

View File

@ -983,13 +983,18 @@ pub(crate) fn suggest_clone_for_ref(
) )
.must_apply_modulo_regions() .must_apply_modulo_regions()
{ {
diag.span_suggestion_verbose( let suggestion = match self.maybe_get_struct_pattern_shorthand_field(expr) {
expr.span.shrink_to_hi(), Some(ident) => format!(": {}.clone()", ident),
"consider using clone here", None => ".clone()".to_string()
".clone()", };
Applicability::MachineApplicable,
); diag.span_suggestion_verbose(
return true; expr.span.shrink_to_hi(),
"consider using clone here",
suggestion,
Applicability::MachineApplicable,
);
return true;
} }
false false
} }
@ -1150,13 +1155,17 @@ pub(crate) fn suggest_option_to_bool(
return false; return false;
} }
diag.span_suggestion( let suggestion = match self.maybe_get_struct_pattern_shorthand_field(expr) {
Some(ident) => format!(": {}.is_some()", ident),
None => ".is_some()".to_string(),
};
diag.span_suggestion_verbose(
expr.span.shrink_to_hi(), expr.span.shrink_to_hi(),
"use `Option::is_some` to test if the `Option` has a value", "use `Option::is_some` to test if the `Option` has a value",
".is_some()", suggestion,
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
true true
} }

View File

@ -159,7 +159,9 @@ fn annotate<F>(
return; return;
} }
let (stab, const_stab, body_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp); let stab = attr::find_stability(&self.tcx.sess, attrs, item_sp);
let const_stab = attr::find_const_stability(&self.tcx.sess, attrs, item_sp);
let body_stab = attr::find_body_stability(&self.tcx.sess, attrs);
let mut const_span = None; let mut const_span = None;
let const_stab = const_stab.map(|(const_stab, const_span_node)| { let const_stab = const_stab.map(|(const_stab, const_span_node)| {
@ -742,8 +744,8 @@ fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
let features = self.tcx.features(); let features = self.tcx.features();
if features.staged_api { if features.staged_api {
let attrs = self.tcx.hir().attrs(item.hir_id()); let attrs = self.tcx.hir().attrs(item.hir_id());
let (stab, const_stab, _) = let stab = attr::find_stability(&self.tcx.sess, attrs, item.span);
attr::find_stability(&self.tcx.sess, attrs, item.span); let const_stab = attr::find_const_stability(&self.tcx.sess, attrs, item.span);
// If this impl block has an #[unstable] attribute, give an // If this impl block has an #[unstable] attribute, give an
// error if all involved types and traits are stable, because // error if all involved types and traits are stable, because

View File

@ -100,18 +100,13 @@ fn render_all(mut self) {
break; break;
} }
let trimmed = line.trim(); match serde_json::from_str(&line) {
if trimmed.starts_with("{") && trimmed.ends_with("}") { Ok(parsed) => self.render_message(parsed),
self.render_message(match serde_json::from_str(&trimmed) { Err(_err) => {
Ok(parsed) => parsed, // Handle non-JSON output, for example when --nocapture is passed.
Err(err) => { print!("{line}");
panic!("failed to parse libtest json output; error: {err}, line: {line:?}"); let _ = std::io::stdout().flush();
} }
});
} else {
// Handle non-JSON output, for example when --nocapture is passed.
print!("{line}");
let _ = std::io::stdout().flush();
} }
} }
} }

View File

@ -87,21 +87,6 @@
box-sizing: border-box; box-sizing: border-box;
} }
/* This part handles the "default" theme being used depending on the system one. */
html {
content: "";
}
@media (prefers-color-scheme: light) {
html {
content: "light";
}
}
@media (prefers-color-scheme: dark) {
html {
content: "dark";
}
}
/* General structure and fonts */ /* General structure and fonts */
body { body {
@ -1538,7 +1523,7 @@ However, it's not needed with smaller screen width because the doc/code block is
/* /*
WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
If you update this line, then you also need to update the line with the same warning If you update this line, then you also need to update the line with the same warning
in storage.js in main.js
*/ */
@media (max-width: 700px) { @media (max-width: 700px) {
/* When linking to an item with an `id` (for instance, by clicking a link in the sidebar, /* When linking to an item with an `id` (for instance, by clicking a link in the sidebar,

View File

@ -4,6 +4,11 @@
"use strict"; "use strict";
// WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
// If you update this line, then you also need to update the media query with the same
// warning in rustdoc.css
window.RUSTDOC_MOBILE_BREAKPOINT = 700;
// Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL // Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL
// for a resource under the root-path, with the resource-suffix. // for a resource under the root-path, with the resource-suffix.
function resourcePath(basename, extension) { function resourcePath(basename, extension) {

View File

@ -8,29 +8,14 @@
const darkThemes = ["dark", "ayu"]; const darkThemes = ["dark", "ayu"];
window.currentTheme = document.getElementById("themeStyle"); window.currentTheme = document.getElementById("themeStyle");
// WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
// If you update this line, then you also need to update the media query with the same
// warning in rustdoc.css
window.RUSTDOC_MOBILE_BREAKPOINT = 700;
const settingsDataset = (function() { const settingsDataset = (function() {
const settingsElement = document.getElementById("default-settings"); const settingsElement = document.getElementById("default-settings");
if (settingsElement === null) { return settingsElement && settingsElement.dataset ? settingsElement.dataset : null;
return null;
}
const dataset = settingsElement.dataset;
if (dataset === undefined) {
return null;
}
return dataset;
})(); })();
function getSettingValue(settingName) { function getSettingValue(settingName) {
const current = getCurrentValue(settingName); const current = getCurrentValue(settingName);
if (current !== null) { if (current === null && settingsDataset !== null) {
return current;
}
if (settingsDataset !== null) {
// See the comment for `default_settings.into_iter()` etc. in // See the comment for `default_settings.into_iter()` etc. in
// `Options::from_matches` in `librustdoc/config.rs`. // `Options::from_matches` in `librustdoc/config.rs`.
const def = settingsDataset[settingName.replace(/-/g,"_")]; const def = settingsDataset[settingName.replace(/-/g,"_")];
@ -38,7 +23,7 @@ function getSettingValue(settingName) {
return def; return def;
} }
} }
return null; return current;
} }
const localStoredTheme = getSettingValue("theme"); const localStoredTheme = getSettingValue("theme");
@ -49,18 +34,16 @@ function hasClass(elem, className) {
} }
function addClass(elem, className) { function addClass(elem, className) {
if (!elem || !elem.classList) { if (elem && elem.classList) {
return; elem.classList.add(className);
} }
elem.classList.add(className);
} }
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
function removeClass(elem, className) { function removeClass(elem, className) {
if (!elem || !elem.classList) { if (elem && elem.classList) {
return; elem.classList.remove(className);
} }
elem.classList.remove(className);
} }
/** /**
@ -127,11 +110,7 @@ function getCurrentValue(name) {
// Rust to the JS. If there is no such element, return null. // Rust to the JS. If there is no such element, return null.
const getVar = (function getVar(name) { const getVar = (function getVar(name) {
const el = document.getElementById("rustdoc-vars"); const el = document.getElementById("rustdoc-vars");
if (el) { return el ? el.attributes["data-" + name].value : null;
return el.attributes["data-" + name].value;
} else {
return null;
}
}); });
function switchTheme(newThemeName, saveTheme) { function switchTheme(newThemeName, saveTheme) {
@ -158,6 +137,9 @@ function switchTheme(newThemeName, saveTheme) {
} }
const updateTheme = (function() { const updateTheme = (function() {
// only listen to (prefers-color-scheme: dark) because light is the default
const mql = window.matchMedia("(prefers-color-scheme: dark)");
/** /**
* Update the current theme to match whatever the current combination of * Update the current theme to match whatever the current combination of
* * the preference for using the system theme * * the preference for using the system theme
@ -177,7 +159,7 @@ const updateTheme = (function() {
const lightTheme = getSettingValue("preferred-light-theme") || "light"; const lightTheme = getSettingValue("preferred-light-theme") || "light";
const darkTheme = getSettingValue("preferred-dark-theme") || "dark"; const darkTheme = getSettingValue("preferred-dark-theme") || "dark";
if (isDarkMode()) { if (mql.matches) {
use(darkTheme, true); use(darkTheme, true);
} else { } else {
// prefers a light theme, or has no preference // prefers a light theme, or has no preference
@ -191,37 +173,7 @@ const updateTheme = (function() {
} }
} }
// This is always updated below to a function () => bool. mql.addEventListener("change", updateTheme);
let isDarkMode;
// Determine the function for isDarkMode, and if we have
// `window.matchMedia`, set up an event listener on the preferred color
// scheme.
//
// Otherwise, fall back to the prefers-color-scheme value CSS captured in
// the "content" property.
if (window.matchMedia) {
// only listen to (prefers-color-scheme: dark) because light is the default
const mql = window.matchMedia("(prefers-color-scheme: dark)");
isDarkMode = () => mql.matches;
if (mql.addEventListener) {
mql.addEventListener("change", updateTheme);
} else {
// This is deprecated, see:
// https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/addListener
mql.addListener(updateTheme);
}
} else {
// fallback to the CSS computed value
const cssContent = getComputedStyle(document.documentElement)
.getPropertyValue("content");
// (Note: the double-quotes come from that this is a CSS value, which
// might be a length, string, etc.)
const cssColorScheme = cssContent || "\"light\"";
isDarkMode = () => (cssColorScheme === "\"dark\"");
}
return updateTheme; return updateTheme;
})(); })();

View File

@ -123,8 +123,35 @@ fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32 {
assert_eq!(wpw.wrapper_ptr_wrapper(), 7); assert_eq!(wpw.wrapper_ptr_wrapper(), 7);
} }
fn raw_ptr_receiver() {
use std::ptr;
trait Foo {
fn foo(self: *const Self) -> &'static str;
}
impl Foo for i32 {
fn foo(self: *const Self) -> &'static str {
"I'm an i32!"
}
}
impl Foo for u32 {
fn foo(self: *const Self) -> &'static str {
"I'm a u32!"
}
}
let null_i32 = ptr::null::<i32>() as *const dyn Foo;
let null_u32 = ptr::null::<u32>() as *const dyn Foo;
assert_eq!("I'm an i32!", null_i32.foo());
assert_eq!("I'm a u32!", null_u32.foo());
}
fn main() { fn main() {
pin_box_dyn(); pin_box_dyn();
stdlib_pointers(); stdlib_pointers();
pointers_and_wrappers(); pointers_and_wrappers();
raw_ptr_receiver();
} }

View File

@ -0,0 +1,48 @@
// This test ensures that the "Auto-hide item methods' documentation" setting is working as
// expected.
define-function: (
"check-setting",
(storage_value, setting_attribute_value, toggle_attribute_value),
block {
assert-local-storage: {"rustdoc-auto-hide-method-docs": |storage_value|}
click: "#settings-menu"
wait-for: "#settings"
assert-property: ("#auto-hide-method-docs", {"checked": |setting_attribute_value|})
assert-attribute: (".toggle.method-toggle", {"open": |toggle_attribute_value|})
}
)
goto: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html"
// We check that the setting is disabled by default.
call-function: ("check-setting", {
"storage_value": null,
"setting_attribute_value": "false",
"toggle_attribute_value": "",
})
// Now we change its value.
click: "#auto-hide-method-docs"
assert-local-storage: {"rustdoc-auto-hide-method-docs": "true"}
// We check that the changes were applied as expected.
reload:
call-function: ("check-setting", {
"storage_value": "true",
"setting_attribute_value": "true",
"toggle_attribute_value": null,
})
// And now we re-disable the setting.
click: "#auto-hide-method-docs"
assert-local-storage: {"rustdoc-auto-hide-method-docs": "false"}
// And we check everything is back the way it was before.
reload:
call-function: ("check-setting", {
"storage_value": "false",
"setting_attribute_value": "false",
"toggle_attribute_value": "",
})

View File

@ -1,5 +1,5 @@
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/wf-bounds.rs:11:22 --> $DIR/wf-bounds.rs:13:22
| |
LL | fn nya() -> impl Wf<Vec<[u8]>>; LL | fn nya() -> impl Wf<Vec<[u8]>>;
| ^^^^^^^^^^^^^ doesn't have a size known at compile-time | ^^^^^^^^^^^^^ doesn't have a size known at compile-time
@ -9,7 +9,7 @@ note: required by a bound in `Vec`
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/wf-bounds.rs:14:23 --> $DIR/wf-bounds.rs:16:23
| |
LL | fn nya2() -> impl Wf<[u8]>; LL | fn nya2() -> impl Wf<[u8]>;
| ^^^^^^^^ doesn't have a size known at compile-time | ^^^^^^^^ doesn't have a size known at compile-time
@ -18,13 +18,23 @@ LL | fn nya2() -> impl Wf<[u8]>;
note: required by a bound in `Wf` note: required by a bound in `Wf`
--> $DIR/wf-bounds.rs:8:10 --> $DIR/wf-bounds.rs:8:10
| |
LL | trait Wf<T> {} LL | trait Wf<T> {
| ^ required by this bound in `Wf` | ^ required by this bound in `Wf`
help: consider relaxing the implicit `Sized` restriction help: consider relaxing the implicit `Sized` restriction
| |
LL | trait Wf<T: ?Sized> {} LL | trait Wf<T: ?Sized> {
| ++++++++ | ++++++++
error: aborting due to 2 previous errors error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/wf-bounds.rs:19:44
|
LL | fn nya3() -> impl Wf<(), Output = impl Wf<Vec<[u8]>>>;
| ^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
note: required by a bound in `Vec`
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`. For more information about this error, try `rustc --explain E0277`.

View File

@ -1,5 +1,5 @@
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/wf-bounds.rs:11:22 --> $DIR/wf-bounds.rs:13:22
| |
LL | fn nya() -> impl Wf<Vec<[u8]>>; LL | fn nya() -> impl Wf<Vec<[u8]>>;
| ^^^^^^^^^^^^^ doesn't have a size known at compile-time | ^^^^^^^^^^^^^ doesn't have a size known at compile-time
@ -9,7 +9,7 @@ note: required by a bound in `Vec`
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/wf-bounds.rs:14:23 --> $DIR/wf-bounds.rs:16:23
| |
LL | fn nya2() -> impl Wf<[u8]>; LL | fn nya2() -> impl Wf<[u8]>;
| ^^^^^^^^ doesn't have a size known at compile-time | ^^^^^^^^ doesn't have a size known at compile-time
@ -18,13 +18,23 @@ LL | fn nya2() -> impl Wf<[u8]>;
note: required by a bound in `Wf` note: required by a bound in `Wf`
--> $DIR/wf-bounds.rs:8:10 --> $DIR/wf-bounds.rs:8:10
| |
LL | trait Wf<T> {} LL | trait Wf<T> {
| ^ required by this bound in `Wf` | ^ required by this bound in `Wf`
help: consider relaxing the implicit `Sized` restriction help: consider relaxing the implicit `Sized` restriction
| |
LL | trait Wf<T: ?Sized> {} LL | trait Wf<T: ?Sized> {
| ++++++++ | ++++++++
error: aborting due to 2 previous errors error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/wf-bounds.rs:19:44
|
LL | fn nya3() -> impl Wf<(), Output = impl Wf<Vec<[u8]>>>;
| ^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
note: required by a bound in `Vec`
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`. For more information about this error, try `rustc --explain E0277`.

View File

@ -5,7 +5,9 @@
#![feature(return_position_impl_trait_in_trait)] #![feature(return_position_impl_trait_in_trait)]
#![allow(incomplete_features)] #![allow(incomplete_features)]
trait Wf<T> {} trait Wf<T> {
type Output;
}
trait Uwu { trait Uwu {
fn nya() -> impl Wf<Vec<[u8]>>; fn nya() -> impl Wf<Vec<[u8]>>;
@ -13,6 +15,9 @@ trait Uwu {
fn nya2() -> impl Wf<[u8]>; fn nya2() -> impl Wf<[u8]>;
//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time
fn nya3() -> impl Wf<(), Output = impl Wf<Vec<[u8]>>>;
//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time
} }
fn main() {} fn main() {}

View File

@ -0,0 +1,29 @@
// run-rustfix
#![allow(dead_code)]
struct Foo {
t: Thing
}
#[derive(Clone)]
struct Thing;
fn test_clone() {
let t = &Thing;
let _f = Foo {
t: t.clone() //~ ERROR mismatched types
};
}
struct Bar {
t: bool
}
fn test_is_some() {
let t = Option::<i32>::Some(1);
let _f = Bar {
t: t.is_some() //~ ERROR mismatched types
};
}
fn main() {}

View File

@ -0,0 +1,29 @@
// run-rustfix
#![allow(dead_code)]
struct Foo {
t: Thing
}
#[derive(Clone)]
struct Thing;
fn test_clone() {
let t = &Thing;
let _f = Foo {
t //~ ERROR mismatched types
};
}
struct Bar {
t: bool
}
fn test_is_some() {
let t = Option::<i32>::Some(1);
let _f = Bar {
t //~ ERROR mismatched types
};
}
fn main() {}

View File

@ -0,0 +1,27 @@
error[E0308]: mismatched types
--> $DIR/issue-108470.rs:14:9
|
LL | t
| ^ expected `Thing`, found `&Thing`
|
help: consider using clone here
|
LL | t: t.clone()
| +++++++++++
error[E0308]: mismatched types
--> $DIR/issue-108470.rs:25:9
|
LL | t
| ^ expected `bool`, found `Option<i32>`
|
= note: expected type `bool`
found enum `Option<i32>`
help: use `Option::is_some` to test if the `Option` has a value
|
LL | t: t.is_some()
| +++++++++++++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.