"lazy_static",
"remove_dir_all",
"serde_json",
+ "snapbox",
"tar",
"termcolor",
- "toml_edit 0.13.4",
+ "toml_edit 0.14.3",
"url 2.2.2",
]
version = "0.0.0"
dependencies = [
"rustc_lexer",
- "rustc_span",
]
[[package]]
pub use UnsafeSource::*;
use crate::ptr::P;
-use crate::token::{self, CommentKind, Delimiter, Token};
+use crate::token::{self, CommentKind, Delimiter, Token, TokenKind};
use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
ExprKind::Paren(..) => ExprPrecedence::Paren,
ExprKind::Try(..) => ExprPrecedence::Try,
ExprKind::Yield(..) => ExprPrecedence::Yield,
+ ExprKind::Yeet(..) => ExprPrecedence::Yeet,
ExprKind::Err => ExprPrecedence::Err,
}
}
/// A `yield`, with an optional value to be yielded.
Yield(Option<P<Expr>>),
+ /// A `do yeet` (aka `throw`/`fail`/`bail`/`raise`/whatever),
+ /// with an optional value to be returned.
+ Yeet(Option<P<Expr>>),
+
/// Placeholder for an expression that wasn't syntactically well formed in some way.
Err,
}
}
/// Arguments passed to an attribute or a function-like macro.
-#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Encodable, Decodable, Debug)]
pub enum MacArgs {
/// No arguments - `#[attr]`.
Empty,
Eq(
/// Span of the `=` token.
Span,
- /// "value" as a nonterminal token.
- Token,
+ /// The "value".
+ MacArgsEq,
),
}
+// The RHS of a `MacArgs::Eq` starts out as an expression. Once macro expansion
+// is completed, all cases end up either as a literal, which is the form used
+// after lowering to HIR, or as an error.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum MacArgsEq {
+ Ast(P<Expr>),
+ Hir(Lit),
+}
+
impl MacArgs {
pub fn delim(&self) -> Option<Delimiter> {
match self {
match self {
MacArgs::Empty => None,
MacArgs::Delimited(dspan, ..) => Some(dspan.entire()),
- MacArgs::Eq(eq_span, token) => Some(eq_span.to(token.span)),
+ MacArgs::Eq(eq_span, MacArgsEq::Ast(expr)) => Some(eq_span.to(expr.span)),
+ MacArgs::Eq(_, MacArgsEq::Hir(lit)) => {
+ unreachable!("in literal form when getting span: {:?}", lit);
+ }
}
}
match self {
MacArgs::Empty => TokenStream::default(),
MacArgs::Delimited(.., tokens) => tokens.clone(),
- MacArgs::Eq(.., token) => TokenTree::Token(token.clone()).into(),
+ MacArgs::Eq(_, MacArgsEq::Ast(expr)) => {
+ // Currently only literals are allowed here. If more complex expression kinds are
+ // allowed in the future, then `nt_to_tokenstream` should be used to extract the
+ // token stream. This will require some cleverness, perhaps with a function
+ // pointer, because `nt_to_tokenstream` is not directly usable from this crate.
+ // It will also require changing the `parse_expr` call in `parse_mac_args_common`
+ // to `parse_expr_force_collect`.
+ if let ExprKind::Lit(lit) = &expr.kind {
+ let token = Token::new(TokenKind::Literal(lit.token), lit.span);
+ TokenTree::Token(token).into()
+ } else {
+ unreachable!("couldn't extract literal when getting inner tokens: {:?}", expr)
+ }
+ }
+ MacArgs::Eq(_, MacArgsEq::Hir(lit)) => {
+ unreachable!("in literal form when getting inner tokens: {:?}", lit)
+ }
}
}
}
}
+impl<CTX> HashStable<CTX> for MacArgs
+where
+ CTX: crate::HashStableContext,
+{
+ fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
+ mem::discriminant(self).hash_stable(ctx, hasher);
+ match self {
+ MacArgs::Empty => {}
+ MacArgs::Delimited(dspan, delim, tokens) => {
+ dspan.hash_stable(ctx, hasher);
+ delim.hash_stable(ctx, hasher);
+ tokens.hash_stable(ctx, hasher);
+ }
+ MacArgs::Eq(_eq_span, MacArgsEq::Ast(expr)) => {
+ unreachable!("hash_stable {:?}", expr);
+ }
+ MacArgs::Eq(eq_span, MacArgsEq::Hir(lit)) => {
+ eq_span.hash_stable(ctx, hasher);
+ lit.hash_stable(ctx, hasher);
+ }
+ }
+ }
+}
+
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum MacDelimiter {
Parenthesis,
use crate::ast;
use crate::ast::{AttrId, AttrItem, AttrKind, AttrStyle, Attribute};
use crate::ast::{Lit, LitKind};
-use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem};
+use crate::ast::{MacArgs, MacArgsEq, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem};
use crate::ast::{Path, PathSegment};
+use crate::ptr::P;
use crate::token::{self, CommentKind, Delimiter, Token};
use crate::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree};
use crate::tokenstream::{DelimSpan, Spacing, TokenTree, TreeAndSpacing};
use crate::tokenstream::{LazyTokenStream, TokenStream};
use crate::util::comments;
+use rustc_data_structures::thin_vec::ThinVec;
use rustc_index::bit_set::GrowableBitSet;
use rustc_span::source_map::BytePos;
use rustc_span::symbol::{sym, Ident, Symbol};
pub fn mac_args(&self, span: Span) -> MacArgs {
match self {
MetaItemKind::Word => MacArgs::Empty,
- MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.to_token()),
+ MetaItemKind::NameValue(lit) => {
+ let expr = P(ast::Expr {
+ id: ast::DUMMY_NODE_ID,
+ kind: ast::ExprKind::Lit(lit.clone()),
+ span: lit.span,
+ attrs: ThinVec::new(),
+ tokens: None,
+ });
+ MacArgs::Eq(span, MacArgsEq::Ast(expr))
+ }
MetaItemKind::List(list) => {
let mut tts = Vec::new();
for (i, item) in list.iter().enumerate() {
fn from_mac_args(args: &MacArgs) -> Option<MetaItemKind> {
match args {
+ MacArgs::Empty => Some(MetaItemKind::Word),
MacArgs::Delimited(_, MacDelimiter::Parenthesis, tokens) => {
MetaItemKind::list_from_tokens(tokens.clone())
}
MacArgs::Delimited(..) => None,
- MacArgs::Eq(_, token) => Lit::from_token(token).ok().map(MetaItemKind::NameValue),
- MacArgs::Empty => Some(MetaItemKind::Word),
+ MacArgs::Eq(_, MacArgsEq::Ast(expr)) => match &expr.kind {
+ ast::ExprKind::Lit(lit) => Some(MetaItemKind::NameValue(lit.clone())),
+ _ => None,
+ },
+ MacArgs::Eq(_, MacArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())),
}
}
visit_delim_span(dspan, vis);
visit_tts(tokens, vis);
}
- MacArgs::Eq(eq_span, token) => {
+ MacArgs::Eq(eq_span, MacArgsEq::Ast(expr)) => {
vis.visit_span(eq_span);
- if T::VISIT_TOKENS {
- visit_token(token, vis);
- } else {
- // The value in `#[key = VALUE]` must be visited as an expression for backward
- // compatibility, so that macros can be expanded in that position.
- match &mut token.kind {
- token::Interpolated(nt) => match Lrc::make_mut(nt) {
- token::NtExpr(expr) => vis.visit_expr(expr),
- t => panic!("unexpected token in key-value attribute: {:?}", t),
- },
- t => panic!("unexpected token in key-value attribute: {:?}", t),
- }
- }
+ vis.visit_expr(expr);
+ }
+ MacArgs::Eq(_, MacArgsEq::Hir(lit)) => {
+ unreachable!("in literal form when visiting mac args eq: {:?}", lit)
}
}
}
}
token::Interpolated(nt) => {
let mut nt = Lrc::make_mut(nt);
- visit_interpolated(&mut nt, vis);
+ visit_nonterminal(&mut nt, vis);
}
_ => {}
}
// contain multiple items, but decided against it when I looked at
// `parse_item_or_view_item` and tried to figure out what I would do with
// multiple items there....
-pub fn visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) {
+pub fn visit_nonterminal<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) {
match nt {
token::NtItem(item) => visit_clobber(item, |item| {
// This is probably okay, because the only visitors likely to
ExprKind::Ret(expr) => {
visit_opt(expr, |expr| vis.visit_expr(expr));
}
+ ExprKind::Yeet(expr) => {
+ visit_opt(expr, |expr| vis.visit_expr(expr));
+ }
ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
ExprKind::Struct(se) => {
/// treat regular and interpolated lifetime identifiers in the same way.
Lifetime(Symbol),
+ /// An embedded AST node, as produced by a macro. This only exists for
+ /// historical reasons. We'd like to get rid of it, for multiple reasons.
+ /// - It's conceptually very strange. Saying a token can contain an AST
+ /// node is like saying, in natural language, that a word can contain a
+ /// sentence.
+ /// - It requires special handling in a bunch of places in the parser.
+ /// - It prevents `Token` from implementing `Copy`.
+ /// It adds complexity and likely slows things down. Please don't add new
+ /// occurrences of this token kind!
Interpolated(Lrc<Nonterminal>),
/// A doc comment token.
}
/// Returns an identifier if this token is an identifier.
+ #[inline]
pub fn ident(&self) -> Option<(Ident, /* is_raw */ bool)> {
- let token = self.uninterpolate();
- match token.kind {
- Ident(name, is_raw) => Some((Ident::new(name, token.span), is_raw)),
+ // We avoid using `Token::uninterpolate` here because it's slow.
+ match &self.kind {
+ &Ident(name, is_raw) => Some((Ident::new(name, self.span), is_raw)),
+ Interpolated(nt) => match **nt {
+ NtIdent(ident, is_raw) => Some((ident, is_raw)),
+ _ => None,
+ },
_ => None,
}
}
/// Returns a lifetime identifier if this token is a lifetime.
+ #[inline]
pub fn lifetime(&self) -> Option<Ident> {
- let token = self.uninterpolate();
- match token.kind {
- Lifetime(name) => Some(Ident::new(name, token.span)),
+ // We avoid using `Token::uninterpolate` here because it's slow.
+ match &self.kind {
+ &Lifetime(name) => Some(Ident::new(name, self.span)),
+ Interpolated(nt) => match **nt {
+ NtLifetime(ident) => Some(ident),
+ _ => None,
+ },
_ => None,
}
}
/// (which happens while parsing the result of macro expansion)?
pub fn is_whole_expr(&self) -> bool {
if let Interpolated(ref nt) = self.kind
- && let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtIdent(..) | NtBlock(_) = **nt
+ && let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = **nt
{
return true;
}
Continue,
Ret,
Yield,
+ Yeet,
Range,
ExprPrecedence::Break |
ExprPrecedence::Continue |
ExprPrecedence::Ret |
- ExprPrecedence::Yield => PREC_JUMP,
+ ExprPrecedence::Yield |
+ ExprPrecedence::Yeet => PREC_JUMP,
// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
// parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
//! those that are created by the expansion of a macro.
use crate::ast::*;
-use crate::token;
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::Span;
ExprKind::Ret(ref optional_expression) => {
walk_list!(visitor, visit_expr, optional_expression);
}
+ ExprKind::Yeet(ref optional_expression) => {
+ walk_list!(visitor, visit_expr, optional_expression);
+ }
ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression),
ExprKind::InlineAsm(ref asm) => walk_inline_asm(visitor, asm),
match args {
MacArgs::Empty => {}
MacArgs::Delimited(_dspan, _delim, _tokens) => {}
- // The value in `#[key = VALUE]` must be visited as an expression for backward
- // compatibility, so that macros can be expanded in that position.
- MacArgs::Eq(_eq_span, token) => match &token.kind {
- token::Interpolated(nt) => match &**nt {
- token::NtExpr(expr) => visitor.visit_expr(expr),
- t => panic!("unexpected token in key-value attribute: {:?}", t),
- },
- t => panic!("unexpected token in key-value attribute: {:?}", t),
- },
+ MacArgs::Eq(_eq_span, MacArgsEq::Ast(expr)) => visitor.visit_expr(expr),
+ MacArgs::Eq(_, MacArgsEq::Hir(lit)) => {
+ unreachable!("in literal form when walking mac args eq: {:?}", lit)
+ }
}
}
let e = e.as_ref().map(|x| self.lower_expr(x));
hir::ExprKind::Ret(e)
}
+ ExprKind::Yeet(ref sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
ExprKind::InlineAsm(ref asm) => {
hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
}
None
}
+ /// If the given expression is a path to a unit struct, returns that path.
+ /// It is not a complete check, but just tries to reject most paths early
+ /// if they are not unit structs.
+ /// Type checking will take care of the full validation later.
+ fn extract_unit_struct_path<'a>(
+ &mut self,
+ expr: &'a Expr,
+ ) -> Option<(&'a Option<QSelf>, &'a Path)> {
+ if let ExprKind::Path(qself, path) = &expr.kind {
+ // Does the path resolve to something disallowed in a unit struct/variant pattern?
+ if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
+ if partial_res.unresolved_segments() == 0
+ && !partial_res.base_res().expected_in_unit_struct_pat()
+ {
+ return None;
+ }
+ }
+ return Some((qself, path));
+ }
+ None
+ }
+
/// Convert the LHS of a destructuring assignment to a pattern.
/// Each sub-assignment is recorded in `assignments`.
fn destructure_assign(
return self.pat_without_dbm(lhs.span, tuple_struct_pat);
}
}
+ // Unit structs and enum variants.
+ ExprKind::Path(..) => {
+ if let Some((qself, path)) = self.extract_unit_struct_path(lhs) {
+ let qpath = self.lower_qpath(
+ lhs.id,
+ qself,
+ path,
+ ParamMode::Optional,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+ );
+ // Destructure like a unit struct.
+ let unit_struct_pat = hir::PatKind::Path(qpath);
+ return self.pat_without_dbm(lhs.span, unit_struct_pat);
+ }
+ }
// Structs.
ExprKind::Struct(se) => {
let field_pats = self.arena.alloc_from_iter(se.fields.iter().map(|f| {
)
}
+ /// Desugar `ExprKind::Yeet` from: `do yeet <expr>` into:
+ /// ```rust
+ /// // If there is an enclosing `try {...}`:
+ /// break 'catch_target FromResidual::from_residual(Yeet(residual)),
+ /// // Otherwise:
+ /// return FromResidual::from_residual(Yeet(residual)),
+ /// ```
+ /// But to simplify this, there's a `from_yeet` lang item function which
+ /// handles the combined `FromResidual::from_residual(Yeet(residual))`.
+ fn lower_expr_yeet(&mut self, span: Span, sub_expr: Option<&Expr>) -> hir::ExprKind<'hir> {
+ // The expression (if present) or `()` otherwise.
+ let (yeeted_span, yeeted_expr) = if let Some(sub_expr) = sub_expr {
+ (sub_expr.span, self.lower_expr(sub_expr))
+ } else {
+ (self.mark_span_with_reason(DesugaringKind::YeetExpr, span, None), self.expr_unit(span))
+ };
+
+ let unstable_span = self.mark_span_with_reason(
+ DesugaringKind::YeetExpr,
+ span,
+ self.allow_try_trait.clone(),
+ );
+
+ let from_yeet_expr = self.wrap_in_try_constructor(
+ hir::LangItem::TryTraitFromYeet,
+ unstable_span,
+ yeeted_expr,
+ yeeted_span,
+ );
+
+ if let Some(catch_node) = self.catch_scope {
+ let target_id = Ok(self.lower_node_id(catch_node));
+ hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr))
+ } else {
+ hir::ExprKind::Ret(Some(from_yeet_expr))
+ }
+ }
+
// =========================================================================
// Helper methods for building HIR.
// =========================================================================
task_context: None,
current_item: None,
captured_lifetimes: None,
- allow_try_trait: Some([sym::try_trait_v2][..].into()),
+ allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()),
allow_gen_future: Some([sym::gen_future][..].into()),
allow_into_future: Some([sym::into_future][..].into()),
};
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
-use rustc_ast::token::{Delimiter, Token};
-use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree};
+use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream};
use rustc_ast::visit;
use rustc_ast::{self as ast, *};
use rustc_ast_pretty::pprust;
)
}
// This is an inert key-value attribute - it will never be visible to macros
- // after it gets lowered to HIR. Therefore, we can synthesize tokens with fake
- // spans to handle nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
- MacArgs::Eq(eq_span, ref token) => {
- // In valid code the value is always representable as a single literal token.
- fn unwrap_single_token(sess: &Session, tokens: TokenStream, span: Span) -> Token {
- if tokens.len() != 1 {
- sess.diagnostic()
- .delay_span_bug(span, "multiple tokens in key-value attribute's value");
- }
- match tokens.into_trees().next() {
- Some(TokenTree::Token(token)) => token,
- Some(TokenTree::Delimited(_, delim, tokens)) => {
- if delim != Delimiter::Invisible {
- sess.diagnostic().delay_span_bug(
- span,
- "unexpected delimiter in key-value attribute's value",
- );
- }
- unwrap_single_token(sess, tokens, span)
- }
- None => Token::dummy(),
+ // after it gets lowered to HIR. Therefore, we can extract literals to handle
+ // nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
+ MacArgs::Eq(eq_span, MacArgsEq::Ast(ref expr)) => {
+ // In valid code the value always ends up as a single literal. Otherwise, a dummy
+ // literal suffices because the error is handled elsewhere.
+ let lit = if let ExprKind::Lit(lit) = &expr.kind {
+ lit.clone()
+ } else {
+ Lit {
+ token: token::Lit::new(token::LitKind::Err, kw::Empty, None),
+ kind: LitKind::Err(kw::Empty),
+ span: DUMMY_SP,
}
- }
-
- let tokens = FlattenNonterminals {
- parse_sess: &self.sess.parse_sess,
- synthesize_tokens: CanSynthesizeMissingTokens::Yes,
- nt_to_tokenstream: self.nt_to_tokenstream,
- }
- .process_token(token.clone());
- MacArgs::Eq(eq_span, unwrap_single_token(self.sess, tokens, token.span))
+ };
+ MacArgs::Eq(eq_span, MacArgsEq::Hir(lit))
+ }
+ MacArgs::Eq(_, MacArgsEq::Hir(ref lit)) => {
+ unreachable!("in literal form when lowering mac args eq: {:?}", lit)
}
}
}
gate_all!(inline_const, "inline-const is experimental");
gate_all!(inline_const_pat, "inline-const in pattern position is experimental");
gate_all!(associated_const_equality, "associated const equality is incomplete");
+ gate_all!(yeet_expr, "`do yeet` expression is experimental");
// All uses of `gate_all!` below this point were added in #65742,
// and subsequently disabled (with the non-early gating readded).
use rustc_ast::util::parser;
use rustc_ast::{self as ast, BlockCheckMode, PatKind, RangeEnd, RangeSyntax};
use rustc_ast::{attr, Term};
-use rustc_ast::{GenericArg, MacArgs};
+use rustc_ast::{GenericArg, MacArgs, MacArgsEq};
use rustc_ast::{GenericBound, SelfKind, TraitBoundModifier};
use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass};
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
true,
span,
),
- MacArgs::Empty | MacArgs::Eq(..) => {
+ MacArgs::Empty => {
self.print_path(&item.path, false, 0);
- if let MacArgs::Eq(_, token) = &item.args {
- self.space();
- self.word_space("=");
- let token_str = self.token_to_string_ext(token, true);
- self.word(token_str);
- }
+ }
+ MacArgs::Eq(_, MacArgsEq::Ast(expr)) => {
+ self.print_path(&item.path, false, 0);
+ self.space();
+ self.word_space("=");
+ let token_str = self.expr_to_string(expr);
+ self.word(token_str);
+ }
+ MacArgs::Eq(_, MacArgsEq::Hir(lit)) => {
+ self.print_path(&item.path, false, 0);
+ self.space();
+ self.word_space("=");
+ let token_str = self.literal_to_string(lit);
+ self.word(token_str);
}
}
self.end();
Self::to_string(|s| s.print_expr(e))
}
+ fn literal_to_string(&self, lit: &ast::Lit) -> String {
+ Self::to_string(|s| s.print_literal(lit))
+ }
+
fn tt_to_string(&self, tt: &TokenTree) -> String {
Self::to_string(|s| s.print_tt(tt, false))
}
// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
pub(super) fn cond_needs_par(expr: &ast::Expr) -> bool {
match expr.kind {
- ast::ExprKind::Break(..) | ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) => true,
+ ast::ExprKind::Break(..)
+ | ast::ExprKind::Closure(..)
+ | ast::ExprKind::Ret(..)
+ | ast::ExprKind::Yeet(..) => true,
_ => parser::contains_exterior_struct_lit(expr),
}
}
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
}
}
+ ast::ExprKind::Yeet(ref result) => {
+ self.word("do");
+ self.word(" ");
+ self.word("yeet");
+ if let Some(ref expr) = *result {
+ self.word(" ");
+ self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+ }
+ }
ast::ExprKind::InlineAsm(ref a) => {
self.word("asm!");
self.print_inline_asm(a);
{
if let ty::FnDef(id, _) = *literal.ty().kind() {
debug!("add_moved_or_invoked_closure_note: id={:?}", id);
- if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
+ if Some(self.infcx.tcx.parent(id)) == self.infcx.tcx.lang_items().fn_once_trait() {
let closure = match args.first() {
Some(Operand::Copy(ref place)) | Some(Operand::Move(ref place))
if target == place.local_or_deref_local() =>
if !parser.errors.is_empty() {
let err = parser.errors.remove(0);
- let err_sp = template_span.from_inner(err.span);
+ let err_sp = template_span.from_inner(InnerSpan::new(err.span.start, err.span.end));
let msg = &format!("invalid asm template string: {}", err.description);
let mut e = ecx.struct_span_err(err_sp, msg);
e.span_label(err_sp, err.label + " in asm template string");
e.note(¬e);
}
if let Some((label, span)) = err.secondary_label {
- let err_sp = template_span.from_inner(span);
+ let err_sp = template_span.from_inner(InnerSpan::new(span.start, span.end));
e.span_label(err_sp, label);
}
e.emit();
curarg = parser.curarg;
- let mut arg_spans = parser.arg_places.iter().map(|span| template_span.from_inner(*span));
+ let mut arg_spans = parser
+ .arg_places
+ .iter()
+ .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end)));
for piece in unverified_pieces {
match piece {
parse::Piece::String(s) => {
Some(idx)
}
}
- parse::ArgumentNamed(name, span) => match args.named_args.get(&name) {
- Some(&idx) => Some(idx),
- None => {
- let msg = format!("there is no argument named `{}`", name);
- ecx.struct_span_err(template_span.from_inner(span), &msg).emit();
- None
+ parse::ArgumentNamed(name, span) => {
+ match args.named_args.get(&Symbol::intern(name)) {
+ Some(&idx) => Some(idx),
+ None => {
+ let msg = format!("there is no argument named `{}`", name);
+ ecx.struct_span_err(
+ template_span
+ .from_inner(InnerSpan::new(span.start, span.end)),
+ &msg,
+ )
+ .emit();
+ None
+ }
}
- },
+ }
};
let mut chars = arg.format.ty.chars();
let span = arg
.format
.ty_span
- .map(|sp| template_sp.from_inner(sp))
+ .map(|sp| template_sp.from_inner(InnerSpan::new(sp.start, sp.end)))
.unwrap_or(template_sp);
ecx.struct_span_err(
span,
let template_num_lines = 1 + template_str.matches('\n').count();
line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines));
} else {
- line_spans.extend(parser.line_spans.iter().map(|span| template_span.from_inner(*span)));
+ line_spans.extend(
+ parser
+ .line_spans
+ .iter()
+ .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))),
+ );
};
}
fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) {
// NOTE: the `unwrap_or` branch is needed in case of invalid format
// arguments, e.g., `format_args!("{foo}")`.
- let lookup = |s: Symbol| *self.names.get(&s).unwrap_or(&0);
+ let lookup = |s: &str| *self.names.get(&Symbol::intern(s)).unwrap_or(&0);
match *p {
parse::String(_) => {}
// it's written second, so it should come after width/precision.
let pos = match arg.position {
parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i),
- parse::ArgumentNamed(s, span) => Named(s, span),
+ parse::ArgumentNamed(s, span) => {
+ Named(Symbol::intern(s), InnerSpan::new(span.start, span.end))
+ }
};
let ty = Placeholder(match arg.format.ty {
"X" => "UpperHex",
_ => {
let fmtsp = self.fmtsp;
- let sp = arg.format.ty_span.map(|sp| fmtsp.from_inner(sp));
+ let sp = arg
+ .format
+ .ty_span
+ .map(|sp| fmtsp.from_inner(InnerSpan::new(sp.start, sp.end)));
let mut err = self.ecx.struct_span_err(
sp.unwrap_or(fmtsp),
&format!("unknown format trait `{}`", arg.format.ty),
}
}
- fn verify_count(&mut self, c: parse::Count) {
+ fn verify_count(&mut self, c: parse::Count<'_>) {
match c {
parse::CountImplied | parse::CountIs(..) => {}
parse::CountIsParam(i) => {
self.verify_arg_type(Exact(i), Count);
}
parse::CountIsName(s, span) => {
- self.verify_arg_type(Named(s, span), Count);
+ self.verify_arg_type(
+ Named(Symbol::intern(s), InnerSpan::new(span.start, span.end)),
+ Count,
+ );
}
}
}
for fmt in &self.arg_with_formatting {
if let Some(span) = fmt.precision_span {
- let span = self.fmtsp.from_inner(span);
+ let span = self.fmtsp.from_inner(InnerSpan::new(span.start, span.end));
match fmt.precision {
parse::CountIsParam(pos) if pos > self.num_args() => {
e.span_label(
}
}
if let Some(span) = fmt.width_span {
- let span = self.fmtsp.from_inner(span);
+ let span = self.fmtsp.from_inner(InnerSpan::new(span.start, span.end));
match fmt.width {
parse::CountIsParam(pos) if pos > self.num_args() => {
e.span_label(
ecx.std_path(&[sym::fmt, sym::rt, sym::v1, s])
}
- fn build_count(&self, c: parse::Count) -> P<ast::Expr> {
+ fn build_count(&self, c: parse::Count<'_>) -> P<ast::Expr> {
let sp = self.macsp;
let count = |c, arg| {
let mut path = Context::rtpath(self.ecx, sym::Count);
if !parser.errors.is_empty() {
let err = parser.errors.remove(0);
let sp = if efmt_kind_is_lit {
- fmt_span.from_inner(err.span)
+ fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end))
} else {
// The format string could be another macro invocation, e.g.:
// format!(concat!("abc", "{}"), 4);
}
if let Some((label, span)) = err.secondary_label {
if efmt_kind_is_lit {
- e.span_label(fmt_span.from_inner(span), label);
+ e.span_label(fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label);
}
}
e.emit();
return DummyResult::raw_expr(sp, true);
}
- let arg_spans = parser.arg_places.iter().map(|span| fmt_span.from_inner(*span)).collect();
+ let arg_spans = parser
+ .arg_places
+ .iter()
+ .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end)))
+ .collect();
let named_pos: FxHashSet<usize> = names.values().cloned().collect();
}
impl ExtraBackendMethods for GccCodegenBackend {
- fn new_metadata<'tcx>(&self, _tcx: TyCtxt<'tcx>, _mod_name: &str) -> Self::Module {
- GccContext {
+ fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) -> Self::Module {
+ let mut mods = GccContext {
context: Context::default(),
- }
- }
-
- fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, mods: &mut Self::Module, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) {
- unsafe { allocator::codegen(tcx, mods, module_name, kind, has_alloc_error_handler) }
+ };
+ unsafe { allocator::codegen(tcx, &mut mods, module_name, kind, has_alloc_error_handler); }
+ mods
}
fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) {
unimplemented!();
}
};
- Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: vec![] })
+ Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: vec![] })
}
fn run_thin_lto(_cgcx: &CodegenContext<Self>, _modules: Vec<(String, Self::ThinBuffer)>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> {
Ok(())
}
- unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: &mut ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
+ fn optimize_fat(_cgcx: &CodegenContext<Self>, _module: &mut ModuleCodegen<Self::Module>) -> Result<(), FatalError> {
+ // TODO(antoyo)
+ Ok(())
+ }
+
+ unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
unimplemented!();
}
unimplemented!();
}
- fn run_lto_pass_manager(_cgcx: &CodegenContext<Self>, _module: &ModuleCodegen<Self::Module>, _config: &ModuleConfig, _thin: bool) -> Result<(), FatalError> {
- // TODO(antoyo)
- Ok(())
- }
-
fn run_link(cgcx: &CodegenContext<Self>, diag_handler: &Handler, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
back::write::link(cgcx, diag_handler, modules)
}
use crate::{llvm_util, LlvmCodegenBackend, ModuleLlvm};
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared};
use rustc_codegen_ssa::back::symbol_export;
-use rustc_codegen_ssa::back::write::{
- CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig,
-};
+use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, TargetMachineFactoryConfig};
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
use rustc_data_structures::fx::FxHashMap;
}
}
- Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: serialized_bitcode })
+ Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: serialized_bitcode })
}
crate struct Linker<'a>(&'a mut llvm::Linker<'a>);
pub(crate) fn run_pass_manager(
cgcx: &CodegenContext<LlvmCodegenBackend>,
diag_handler: &Handler,
- module: &ModuleCodegen<ModuleLlvm>,
- config: &ModuleConfig,
+ module: &mut ModuleCodegen<ModuleLlvm>,
thin: bool,
) -> Result<(), FatalError> {
let _timer = cgcx.prof.extra_verbose_generic_activity("LLVM_lto_optimize", &*module.name);
+ let config = cgcx.config(module.kind);
// Now we have one massive module inside of llmod. Time to run the
// LTO-specific optimization passes that LLVM provides.
}
pub unsafe fn optimize_thin_module(
- thin_module: &mut ThinModule<LlvmCodegenBackend>,
+ thin_module: ThinModule<LlvmCodegenBackend>,
cgcx: &CodegenContext<LlvmCodegenBackend>,
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
let diag_handler = cgcx.create_diag_handler();
// that LLVM Context and Module.
let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
let llmod_raw = parse_module(llcx, module_name, thin_module.data(), &diag_handler)? as *const _;
- let module = ModuleCodegen {
+ let mut module = ModuleCodegen {
module_llvm: ModuleLlvm { llmod_raw, llcx, tm },
name: thin_module.name().to_string(),
kind: ModuleKind::Regular,
// little differently.
{
info!("running thin lto passes over {}", module.name);
- let config = cgcx.config(module.kind);
- run_pass_manager(cgcx, &diag_handler, &module, config, true)?;
+ run_pass_manager(cgcx, &diag_handler, &mut module, true)?;
save_temp_bitcode(cgcx, &module, "thin-lto-after-pm");
}
}
}
pub fn get_namespace_for_item<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DIScope {
- item_namespace(cx, cx.tcx.parent(def_id).expect("get_namespace_for_item: missing parent?"))
+ item_namespace(cx, cx.tcx.parent(def_id))
}
#[derive(Debug, PartialEq, Eq)]
}
impl ExtraBackendMethods for LlvmCodegenBackend {
- fn new_metadata(&self, tcx: TyCtxt<'_>, mod_name: &str) -> ModuleLlvm {
- ModuleLlvm::new_metadata(tcx, mod_name)
- }
-
fn codegen_allocator<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
- module_llvm: &mut ModuleLlvm,
module_name: &str,
kind: AllocatorKind,
has_alloc_error_handler: bool,
- ) {
- unsafe { allocator::codegen(tcx, module_llvm, module_name, kind, has_alloc_error_handler) }
+ ) -> ModuleLlvm {
+ let mut module_llvm = ModuleLlvm::new_metadata(tcx, module_name);
+ unsafe {
+ allocator::codegen(tcx, &mut module_llvm, module_name, kind, has_alloc_error_handler);
+ }
+ module_llvm
}
fn compile_codegen_unit(
&self,
) -> Result<(), FatalError> {
back::write::optimize(cgcx, diag_handler, module, config)
}
+ fn optimize_fat(
+ cgcx: &CodegenContext<Self>,
+ module: &mut ModuleCodegen<Self::Module>,
+ ) -> Result<(), FatalError> {
+ let diag_handler = cgcx.create_diag_handler();
+ back::lto::run_pass_manager(cgcx, &diag_handler, module, false)
+ }
unsafe fn optimize_thin(
cgcx: &CodegenContext<Self>,
- thin: &mut ThinModule<Self>,
+ thin: ThinModule<Self>,
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
back::lto::optimize_thin_module(thin, cgcx)
}
fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
(module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod()))
}
- fn run_lto_pass_manager(
- cgcx: &CodegenContext<Self>,
- module: &ModuleCodegen<Self::Module>,
- config: &ModuleConfig,
- thin: bool,
- ) -> Result<(), FatalError> {
- let diag_handler = cgcx.create_diag_handler();
- back::lto::run_pass_manager(cgcx, &diag_handler, module, config, thin)
- }
}
unsafe impl Send for LlvmCodegenBackend {} // Llvm is on a per-thread basis
// This change is somewhat breaking in practice due to local static libraries being linked
// as whole-archive (#85144), so removing whole-archive may be a pre-requisite.
if sess.opts.debugging_opts.link_native_libraries {
- add_local_native_libraries(cmd, sess, codegen_results, crate_type);
+ add_local_native_libraries(cmd, sess, codegen_results);
}
// Upstream rust libraries and their nobundle static libraries
add_rpath_args(cmd, sess, codegen_results, out_filename);
}
-// A dylib may reexport symbols from the linked rlib or native static library.
-// Even if some symbol is reexported it's still not necessarily counted as used and may be
-// dropped, at least with `ld`-like ELF linkers. So we have to link some rlibs and static
-// libraries as whole-archive to avoid losing reexported symbols.
-// FIXME: Find a way to mark reexported symbols as used and avoid this use of whole-archive.
-fn default_to_whole_archive(sess: &Session, crate_type: CrateType, cmd: &dyn Linker) -> bool {
- crate_type == CrateType::Dylib
- && !(sess.target.limit_rdylib_exports && cmd.exported_symbol_means_used_symbol())
-}
-
/// # Native library linking
///
/// User-supplied library search paths (-L on the command line). These are the same paths used to
cmd: &mut dyn Linker,
sess: &Session,
codegen_results: &CodegenResults,
- crate_type: CrateType,
) {
let filesearch = sess.target_filesearch(PathKind::All);
for search_path in filesearch.search_paths() {
}
NativeLibKind::Static { whole_archive, bundle, .. } => {
if whole_archive == Some(true)
- || (whole_archive == None && default_to_whole_archive(sess, crate_type, cmd))
// Backward compatibility case: this can be a rlib (so `+whole-archive` cannot
// be added explicitly if necessary, see the error in `fn link_rlib`) compiled
// as an executable due to `--test`. Use whole-archive implicitly, like before
let src = &codegen_results.crate_info.used_crate_source[&cnum];
match data[cnum.as_usize() - 1] {
_ if codegen_results.crate_info.profiler_runtime == Some(cnum) => {
- add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum);
+ add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, cnum);
}
// compiler-builtins are always placed last to ensure that they're
// linked correctly.
}
Linkage::NotLinked | Linkage::IncludedFromDylib => {}
Linkage::Static => {
- add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum);
+ add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, cnum);
// Link static native libs with "-bundle" modifier only if the crate they originate from
// is being linked statically to the current crate. If it's linked dynamically
lib.kind
{
let verbatim = lib.verbatim.unwrap_or(false);
- if whole_archive == Some(true)
- || (whole_archive == None
- && default_to_whole_archive(sess, crate_type, cmd))
- {
+ if whole_archive == Some(true) {
cmd.link_whole_staticlib(
name,
verbatim,
// was already "included" in a dylib (e.g., `libstd` when `-C prefer-dynamic`
// is used)
if let Some(cnum) = compiler_builtins {
- add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum);
+ add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, cnum);
}
// Converts a library file-stem into a cc -l argument
sess: &'a Session,
codegen_results: &CodegenResults,
tmpdir: &Path,
- crate_type: CrateType,
cnum: CrateNum,
) {
let src = &codegen_results.crate_info.used_crate_source[&cnum];
let cratepath = &src.rlib.as_ref().unwrap().0;
let mut link_upstream = |path: &Path| {
- // We don't want to include the whole compiler-builtins crate (e.g., compiler-rt)
- // regardless of the default because it'll get repeatedly linked anyway.
- let path = fix_windows_verbatim_for_gcc(path);
- if default_to_whole_archive(sess, crate_type, cmd)
- && codegen_results.crate_info.compiler_builtins != Some(cnum)
- {
- cmd.link_whole_rlib(&path);
- } else {
- cmd.link_rlib(&path);
- }
+ cmd.link_rlib(&fix_windows_verbatim_for_gcc(path));
};
// See the comment above in `link_staticlib` and `link_rlib` for why if
fn no_crt_objects(&mut self);
fn no_default_libraries(&mut self);
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]);
- fn exported_symbol_means_used_symbol(&self) -> bool {
- true
- }
fn subsystem(&mut self, subsystem: &str);
fn group_start(&mut self);
fn group_end(&mut self);
}
}
- fn exported_symbol_means_used_symbol(&self) -> bool {
- self.sess.target.is_like_windows || self.sess.target.is_like_osx
- }
-
fn subsystem(&mut self, subsystem: &str) {
self.linker_arg("--subsystem");
self.linker_arg(&subsystem);
return;
}
- fn exported_symbol_means_used_symbol(&self) -> bool {
- false
- }
-
fn subsystem(&mut self, subsystem: &str) {
self.cmd.arg(&format!("--subsystem {}", subsystem));
}
crate_type: CrateType,
) -> Vec<(String, SymbolExportKind)> {
match crate_type {
- CrateType::Executable | CrateType::Cdylib => (),
- CrateType::Staticlib | CrateType::ProcMacro | CrateType::Rlib | CrateType::Dylib => {
+ CrateType::Executable | CrateType::Cdylib | CrateType::Dylib => (),
+ CrateType::Staticlib | CrateType::ProcMacro | CrateType::Rlib => {
return Vec::new();
}
}
pub enum LtoModuleCodegen<B: WriteBackendMethods> {
Fat {
- module: Option<ModuleCodegen<B::Module>>,
+ module: ModuleCodegen<B::Module>,
_serialized_bitcode: Vec<SerializedModule<B::ModuleBuffer>>,
},
/// It's intended that the module returned is immediately code generated and
/// dropped, and then this LTO module is dropped.
pub unsafe fn optimize(
- &mut self,
+ self,
cgcx: &CodegenContext<B>,
) -> Result<ModuleCodegen<B::Module>, FatalError> {
- match *self {
- LtoModuleCodegen::Fat { ref mut module, .. } => {
- let module = module.take().unwrap();
- {
- let config = cgcx.config(module.kind);
- B::run_lto_pass_manager(cgcx, &module, config, false)?;
- }
+ match self {
+ LtoModuleCodegen::Fat { mut module, .. } => {
+ B::optimize_fat(cgcx, &mut module)?;
Ok(module)
}
- LtoModuleCodegen::Thin(ref mut thin) => B::optimize_thin(cgcx, thin),
+ LtoModuleCodegen::Thin(thin) => B::optimize_thin(cgcx, thin),
}
}
fn execute_lto_work_item<B: ExtraBackendMethods>(
cgcx: &CodegenContext<B>,
- mut module: lto::LtoModuleCodegen<B>,
+ module: lto::LtoModuleCodegen<B>,
module_config: &ModuleConfig,
) -> Result<WorkItemResult<B>, FatalError> {
let module = unsafe { module.optimize(cgcx)? };
} else if let Some(kind) = tcx.allocator_kind(()) {
let llmod_id =
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string();
- let mut module_llvm = backend.new_metadata(tcx, &llmod_id);
- tcx.sess.time("write_allocator_module", || {
- backend.codegen_allocator(
- tcx,
- &mut module_llvm,
- &llmod_id,
- kind,
- tcx.lang_items().oom().is_some(),
- )
+ let module_llvm = tcx.sess.time("write_allocator_module", || {
+ backend.codegen_allocator(tcx, &llmod_id, kind, tcx.lang_items().oom().is_some())
});
Some(ModuleCodegen { name: llmod_id, module_llvm, kind: ModuleKind::Allocator })
}
// If there exists a local definition that dominates all uses of that local,
- // the definition should be visited first. Traverse blocks in preorder which
+ // the definition should be visited first. Traverse blocks in an order that
// is a topological sort of dominance partial order.
- for (bb, data) in traversal::preorder(&mir) {
+ for (bb, data) in traversal::reverse_postorder(&mir) {
analyzer.visit_basic_block_data(bb, data);
}
}
pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Send + Sync {
- fn new_metadata(&self, sess: TyCtxt<'_>, mod_name: &str) -> Self::Module;
fn codegen_allocator<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
- module_llvm: &mut Self::Module,
module_name: &str,
kind: AllocatorKind,
has_alloc_error_handler: bool,
- );
+ ) -> Self::Module;
/// This generates the codegen unit and returns it along with
/// a `u64` giving an estimate of the unit's processing cost.
fn compile_codegen_unit(
module: &ModuleCodegen<Self::Module>,
config: &ModuleConfig,
) -> Result<(), FatalError>;
+ fn optimize_fat(
+ cgcx: &CodegenContext<Self>,
+ llmod: &mut ModuleCodegen<Self::Module>,
+ ) -> Result<(), FatalError>;
unsafe fn optimize_thin(
cgcx: &CodegenContext<Self>,
- thin: &mut ThinModule<Self>,
+ thin: ThinModule<Self>,
) -> Result<ModuleCodegen<Self::Module>, FatalError>;
unsafe fn codegen(
cgcx: &CodegenContext<Self>,
) -> Result<CompiledModule, FatalError>;
fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer);
fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer);
- fn run_lto_pass_manager(
- cgcx: &CodegenContext<Self>,
- llmod: &ModuleCodegen<Self::Module>,
- config: &ModuleConfig,
- thin: bool,
- ) -> Result<(), FatalError>;
}
pub trait ThinBufferMethods: Send + Sync {
}
pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
- let parent_id = tcx.local_parent(def_id).unwrap();
+ let parent_id = tcx.local_parent(def_id);
tcx.def_kind(parent_id) == DefKind::Impl
&& tcx.impl_constness(parent_id) == hir::Constness::Const
}
} else {
None
};
- let parent_self_ty = tcx
- .parent(method_did)
- .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl)
+ let parent_did = tcx.parent(method_did);
+ let parent_self_ty = (tcx.def_kind(parent_did) == rustc_hir::def::DefKind::Impl)
+ .then_some(parent_did)
.and_then(|did| match tcx.type_of(did).kind() {
ty::Adt(def, ..) => Some(def.did()),
_ => None,
Erroneous code example:
-```ignore (limited to a warning during 2018 edition development)
+```rust2018,compile_fail,E0705
#![feature(rust_2018_preview)]
#![feature(test_2018_feature)] // error: the feature
// `test_2018_feature` is
}
// Replace the meta-var with the matched token tree from the invocation.
- mbe::TokenTree::MetaVar(mut sp, mut orignal_ident) => {
+ mbe::TokenTree::MetaVar(mut sp, mut original_ident) => {
// Find the matched nonterminal from the macro invocation, and use it to replace
// the meta-var.
- let ident = MacroRulesNormalizedIdent::new(orignal_ident);
+ let ident = MacroRulesNormalizedIdent::new(original_ident);
if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
match cur_matched {
MatchedTokenTree(ref tt) => {
// If we aren't able to match the meta-var, we push it back into the result but
// with modified syntax context. (I believe this supports nested macros).
marker.visit_span(&mut sp);
- marker.visit_ident(&mut orignal_ident);
+ marker.visit_ident(&mut original_ident);
result.push(TokenTree::token(token::Dollar, sp).into());
- result.push(TokenTree::Token(Token::from_ast_ident(orignal_ident)).into());
+ result.push(TokenTree::Token(Token::from_ast_ident(original_ident)).into());
}
}
(active, used_with_arg, "1.60.0", Some(93798), None),
/// Allows `extern "wasm" fn`
(active, wasm_abi, "1.53.0", Some(83788), None),
+ /// Allows `do yeet` expressions
+ (active, yeet_expr, "1.62.0", Some(96373), None),
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
// Features are listed in alphabetical order. Tidy will fail if you don't keep it this way.
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
pub fn expected_in_tuple_struct_pat(&self) -> bool {
matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..))
}
+
+ /// Returns whether such a resolved path can occur in a unit struct/variant pattern
+ pub fn expected_in_unit_struct_pat(&self) -> bool {
+ matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Const), _) | Res::SelfCtor(..))
+ }
}
TryTraitFromResidual, sym::from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
}
}
+ #[inline]
+ pub fn iter(&self) -> ChunkedBitIter<'_, T> {
+ ChunkedBitIter::new(self)
+ }
+
/// Insert `elem`. Returns whether the set has changed.
pub fn insert(&mut self, elem: T) -> bool {
assert!(elem.index() < self.domain_size);
}
}
+pub struct ChunkedBitIter<'a, T: Idx> {
+ index: usize,
+ bitset: &'a ChunkedBitSet<T>,
+}
+
+impl<'a, T: Idx> ChunkedBitIter<'a, T> {
+ #[inline]
+ fn new(bitset: &'a ChunkedBitSet<T>) -> ChunkedBitIter<'a, T> {
+ ChunkedBitIter { index: 0, bitset }
+ }
+}
+
+impl<'a, T: Idx> Iterator for ChunkedBitIter<'a, T> {
+ type Item = T;
+ fn next(&mut self) -> Option<T> {
+ while self.index < self.bitset.domain_size() {
+ let elem = T::new(self.index);
+ let chunk = &self.bitset.chunks[chunk_index(elem)];
+ match &chunk {
+ Zeros(chunk_domain_size) => {
+ self.index += *chunk_domain_size as usize;
+ }
+ Ones(_chunk_domain_size) => {
+ self.index += 1;
+ return Some(elem);
+ }
+ Mixed(_chunk_domain_size, _, words) => loop {
+ let elem = T::new(self.index);
+ self.index += 1;
+ let (word_index, mask) = chunk_word_index_and_mask(elem);
+ if (words[word_index] & mask) != 0 {
+ return Some(elem);
+ }
+ if self.index % CHUNK_BITS == 0 {
+ break;
+ }
+ },
+ }
+ }
+ None
+ }
+}
+
impl Chunk {
#[cfg(test)]
fn assert_valid(&self) {
b10000b.assert_valid();
}
+#[test]
+fn chunked_bitset_iter() {
+ fn with_elements(elements: &[usize], domain_size: usize) -> ChunkedBitSet<usize> {
+ let mut s = ChunkedBitSet::new_empty(domain_size);
+ for &e in elements {
+ s.insert(e);
+ }
+ s
+ }
+
+ // Empty
+ let vec: Vec<usize> = Vec::new();
+ let bit = with_elements(&vec, 9000);
+ assert_eq!(vec, bit.iter().collect::<Vec<_>>());
+
+ // Filled
+ let n = 10000;
+ let vec: Vec<usize> = (0..n).collect();
+ let bit = with_elements(&vec, n);
+ assert_eq!(vec, bit.iter().collect::<Vec<_>>());
+
+ // Filled with trailing zeros
+ let n = 10000;
+ let vec: Vec<usize> = (0..n).collect();
+ let bit = with_elements(&vec, 2 * n);
+ assert_eq!(vec, bit.iter().collect::<Vec<_>>());
+
+ // Mixed
+ let n = 12345;
+ let vec: Vec<usize> = vec![0, 1, 2, 2010, 2047, 2099, 6000, 6002, 6004];
+ let bit = with_elements(&vec, n);
+ assert_eq!(vec, bit.iter().collect::<Vec<_>>());
+}
+
#[test]
fn grow() {
let mut set: GrowableBitSet<usize> = GrowableBitSet::with_capacity(65);
impl InferenceDiagnosticsParentData {
fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> {
- let parent_def_id = tcx.parent(def_id)?;
+ let parent_def_id = tcx.parent(def_id);
let parent_name =
tcx.def_key(parent_def_id).disambiguated_data.data.get_opt_name()?.to_string();
if let Some((DefKind::AssocFn, def_id)) =
self.in_progress_typeck_results?.borrow().type_dependent_def(hir_id)
{
- return self
- .tcx
- .parent(def_id)
- .filter(|&parent_def_id| self.tcx.is_trait(parent_def_id));
+ let parent_def_id = self.tcx.parent(def_id);
+ return self.tcx.is_trait(parent_def_id).then_some(parent_def_id);
}
None
let (id, bound_region) = match *anon_region {
ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
ty::ReEarlyBound(ebr) => {
- (tcx.parent(ebr.def_id).unwrap(), ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name))
+ (tcx.parent(ebr.def_id), ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name))
}
_ => return None, // not a free region
};
if n_arguments > 0 && fmt_parser.errors.is_empty() {
let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] {
[] => vec![fmt_span],
- v => v.iter().map(|span| fmt_span.from_inner(*span)).collect(),
+ v => v
+ .iter()
+ .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end)))
+ .collect(),
};
cx.struct_span_lint(NON_FMT_PANICS, arg_spans, |lint| {
let mut l = lint.build(match n_arguments {
&& let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def()
// skip extension traits, only lint functions from the standard library
&& cx.tcx.trait_id_of_impl(impl_did).is_none()
- && let Some(parent) = cx.tcx.parent(adt.did())
+ && let parent = cx.tcx.parent(adt.did())
&& cx.tcx.is_diagnostic_item(sym::atomic_mod, parent)
&& ATOMIC_TYPES.contains(&cx.tcx.item_name(adt.did()))
{
orderings.iter().any(|ordering| {
tcx.item_name(did) == *ordering && {
let parent = tcx.parent(did);
- parent == atomic_ordering
+ Some(parent) == atomic_ordering
// needed in case this is a ctor, not a variant
- || parent.map_or(false, |parent| tcx.parent(parent) == atomic_ordering)
+ || tcx.opt_parent(parent) == atomic_ordering
}
})
}
let def_kind = self.tcx.def_kind(def_id);
match def_kind {
DefKind::Trait | DefKind::TraitAlias => def_id,
- DefKind::TyParam | DefKind::ConstParam => self.tcx.local_parent(def_id).unwrap(),
+ DefKind::TyParam | DefKind::ConstParam => self.tcx.local_parent(def_id),
_ => bug!("ty_param_owner: {:?} is a {:?} not a type parameter", def_id, def_kind),
}
}
feature: Symbol,
) -> Option<(Span, String, String, Applicability)> {
if feature == sym::allocator_api {
- if let Some(trait_) = tcx.parent(def_id) {
+ if let Some(trait_) = tcx.opt_parent(def_id) {
if tcx.is_diagnostic_item(sym::Vec, trait_) {
let sm = tcx.sess.parse_sess.source_map();
let inner_types = sm.span_extend_to_prev_char(span, '<', true);
/// A temporary created during the creation of an aggregate
/// (e.g. a temporary for `foo` in `MyStruct { my_field: foo }`)
AggregateTemp,
+ /// A temporary created during the pass `Derefer` to avoid it's retagging
+ DerefTemp,
}
impl<'tcx> LocalDecl<'tcx> {
from_end: bool,
},
- /// "Downcast" to a variant of an ADT. Currently, we only introduce
- /// this for ADTs with more than one variant. It may be better to
- /// just introduce it always, or always for enums.
+ /// "Downcast" to a variant of an enum or a generator.
///
/// The included Symbol is the name of the variant, used for printing MIR.
Downcast(Option<Symbol>, VariantIdx),
UnaryOp(UnOp, Operand<'tcx>),
/// Computes the discriminant of the place, returning it as an integer of type
- /// [`discriminant_ty`].
+ /// [`discriminant_ty`]. Returns zero for types without discriminant.
///
/// The validity requirements for the underlying value are undecided for this rvalue, see
/// [#91095]. Note too that the value of the discriminant is not the same thing as the
/// variant index; use [`discriminant_for_variant`] to convert.
///
- /// For types defined in the source code as enums, this is well behaved. This is also well
- /// formed for other types, but yields no particular value - there is no reason it couldn't be
- /// defined to yield eg zero though.
- ///
/// [`discriminant_ty`]: crate::ty::Ty::discriminant_ty
/// [#91095]: https://github.com/rust-lang/rust/issues/91095
/// [`discriminant_for_variant`]: crate::ty::Ty::discriminant_for_variant
Location { block: bb, statement_index: offset }
}
- pub fn new_temp(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
+ pub fn new_local_with_info(
+ &mut self,
+ ty: Ty<'tcx>,
+ span: Span,
+ local_info: Option<Box<LocalInfo<'tcx>>>,
+ ) -> Local {
let index = self.next_local;
self.next_local += 1;
- self.new_locals.push(LocalDecl::new(ty, span));
+ let mut new_decl = LocalDecl::new(ty, span);
+ new_decl.local_info = local_info;
+ self.new_locals.push(new_decl);
Local::new(index as usize)
}
+ pub fn new_temp(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
+ self.new_local_with_info(ty, span, None)
+ }
+
pub fn new_internal(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
let index = self.next_local;
self.next_local += 1;
#[derive(Copy, Clone, Debug, TypeFoldable)]
pub struct PlaceTy<'tcx> {
pub ty: Ty<'tcx>,
- /// Downcast to a particular variant of an enum, if included.
+ /// Downcast to a particular variant of an enum or a generator, if included.
pub variant_index: Option<VariantIdx>,
}
(free_region.scope.expect_local(), free_region.bound_region)
}
ty::ReEarlyBound(ref ebr) => (
- self.parent(ebr.def_id).unwrap().expect_local(),
+ self.local_parent(ebr.def_id.expect_local()),
ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name),
),
_ => return None, // not a free region
| Placeholder(_)
| Error(_) => false,
Opaque(did, substs) => {
- let parent = tcx.parent(*did).expect("opaque types always have a parent");
+ let parent = tcx.parent(*did);
if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = tcx.def_kind(parent)
&& let Opaque(parent_did, _) = tcx.type_of(parent).kind()
&& parent_did == did
}
pub trait DefIdTree: Copy {
- fn parent(self, id: DefId) -> Option<DefId>;
+ fn opt_parent(self, id: DefId) -> Option<DefId>;
#[inline]
- fn local_parent(self, id: LocalDefId) -> Option<LocalDefId> {
- Some(self.parent(id.to_def_id())?.expect_local())
+ #[track_caller]
+ fn parent(self, id: DefId) -> DefId {
+ match self.opt_parent(id) {
+ Some(id) => id,
+ // not `unwrap_or_else` to avoid breaking caller tracking
+ None => bug!("{id:?} doesn't have a parent"),
+ }
+ }
+
+ #[inline]
+ #[track_caller]
+ fn opt_local_parent(self, id: LocalDefId) -> Option<LocalDefId> {
+ self.opt_parent(id.to_def_id()).map(DefId::expect_local)
+ }
+
+ #[inline]
+ #[track_caller]
+ fn local_parent(self, id: LocalDefId) -> LocalDefId {
+ self.parent(id.to_def_id()).expect_local()
}
fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool {
}
while descendant != ancestor {
- match self.parent(descendant) {
+ match self.opt_parent(descendant) {
Some(parent) => descendant = parent,
None => return false,
}
}
impl<'tcx> DefIdTree for TyCtxt<'tcx> {
- fn parent(self, id: DefId) -> Option<DefId> {
+ #[inline]
+ fn opt_parent(self, id: DefId) -> Option<DefId> {
self.def_key(id).parent.map(|index| DefId { index, ..id })
}
}
pub fn expect_variant_res(self, res: Res) -> &'tcx VariantDef {
match res {
Res::Def(DefKind::Variant, did) => {
- let enum_did = self.parent(did).unwrap();
+ let enum_did = self.parent(did);
self.adt_def(enum_did).variant_with_id(did)
}
Res::Def(DefKind::Struct | DefKind::Union, did) => self.adt_def(did).non_enum_variant(),
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_did) => {
- let variant_did = self.parent(variant_ctor_did).unwrap();
- let enum_did = self.parent(variant_did).unwrap();
+ let variant_did = self.parent(variant_ctor_did);
+ let enum_did = self.parent(variant_did);
self.adt_def(enum_did).variant_with_ctor_id(variant_ctor_did)
}
Res::Def(DefKind::Ctor(CtorOf::Struct, ..), ctor_did) => {
- let struct_did = self.parent(ctor_did).expect("struct ctor has no parent");
+ let struct_did = self.parent(ctor_did);
self.adt_def(struct_did).non_enum_variant()
}
_ => bug!("expect_variant_res used with unexpected res {:?}", res),
// as the trait.
let in_self_mod = match characteristic_def_id_of_type(self_ty) {
None => false,
- Some(ty_def_id) => self.tcx().parent(ty_def_id) == Some(parent_def_id),
+ Some(ty_def_id) => self.tcx().parent(ty_def_id) == parent_def_id,
};
let in_trait_mod = match impl_trait_ref {
None => false,
- Some(trait_ref) => self.tcx().parent(trait_ref.def_id) == Some(parent_def_id),
+ Some(trait_ref) => self.tcx().parent(trait_ref.def_id) == parent_def_id,
};
if !in_self_mod && !in_trait_mod {
return Ok((self, false));
};
- let actual_parent = self.tcx().parent(def_id);
+ let actual_parent = self.tcx().opt_parent(def_id);
debug!(
"try_print_visible_def_path: visible_parent={:?} actual_parent={:?}",
visible_parent, actual_parent,
return Ok(self);
}
- let parent = self.tcx().parent(def_id).expect("opaque types always have a parent");
+ let parent = self.tcx().parent(def_id);
match self.tcx().def_kind(parent) {
DefKind::TyAlias | DefKind::AssocTy => {
if let ty::Opaque(d, _) = *self.tcx().type_of(parent).kind() {
/// function might return the `DefId` of a closure.
pub fn free_region_binding_scope(self, tcx: TyCtxt<'_>) -> DefId {
match *self {
- ty::ReEarlyBound(br) => tcx.parent(br.def_id).unwrap(),
+ ty::ReEarlyBound(br) => tcx.parent(br.def_id),
ty::ReFree(fr) => fr.scope,
_ => bug!("free_region_binding_scope invoked on inappropriate region: {:?}", self),
}
pub fn res_generics_def_id(self, res: Res) -> Option<DefId> {
match res {
Res::Def(DefKind::Ctor(CtorOf::Variant, _), def_id) => {
- Some(self.parent(def_id).and_then(|def_id| self.parent(def_id)).unwrap())
+ Some(self.parent(self.parent(def_id)))
}
Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Struct, _), def_id) => {
- Some(self.parent(def_id).unwrap())
+ Some(self.parent(def_id))
}
// Other `DefKind`s don't have generics and would ICE when calling
// `generics_of`.
pub fn typeck_root_def_id(self, def_id: DefId) -> DefId {
let mut def_id = def_id;
while self.is_typeck_child(def_id) {
- def_id = self.parent(def_id).unwrap_or_else(|| {
- bug!("closure {:?} has no parent", def_id);
- });
+ def_id = self.parent(def_id);
}
def_id
}
//! See docs in build/expr/mod.rs
use crate::build::expr::category::Category;
-use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_middle::thir::*;
expr: &Expr<'tcx>,
) -> BlockAnd<Operand<'tcx>> {
let local_scope = self.local_scope();
- self.as_operand(block, Some(local_scope), expr, None)
+ self.as_operand(block, Some(local_scope), expr, None, NeedsTemporary::Maybe)
}
/// Returns an operand suitable for use until the end of the current scope expression and
///
/// Like `as_local_call_operand`, except that the argument will
/// not be valid once `scope` ends.
+ #[instrument(level = "debug", skip(self, scope))]
crate fn as_operand(
&mut self,
mut block: BasicBlock,
scope: Option<region::Scope>,
expr: &Expr<'tcx>,
local_info: Option<Box<LocalInfo<'tcx>>>,
+ needs_temporary: NeedsTemporary,
) -> BlockAnd<Operand<'tcx>> {
- debug!("as_operand(block={:?}, expr={:?} local_info={:?})", block, expr, local_info);
let this = self;
if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
let source_info = this.source_info(expr.span);
let region_scope = (region_scope, source_info);
return this.in_scope(region_scope, lint_level, |this| {
- this.as_operand(block, scope, &this.thir[value], local_info)
+ this.as_operand(block, scope, &this.thir[value], local_info, needs_temporary)
});
}
let category = Category::of(&expr.kind).unwrap();
- debug!("as_operand: category={:?} for={:?}", category, expr.kind);
+ debug!(?category, ?expr.kind);
match category {
- Category::Constant => {
+ Category::Constant if let NeedsTemporary::No = needs_temporary || !expr.ty.needs_drop(this.tcx, this.param_env) => {
let constant = this.as_constant(expr);
block.and(Operand::Constant(Box::new(constant)))
}
- Category::Place | Category::Rvalue(..) => {
+ Category::Constant | Category::Place | Category::Rvalue(..) => {
let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut));
if this.local_decls[operand].local_info.is_none() {
this.local_decls[operand].local_info = local_info;
}
}
- this.as_operand(block, scope, expr, None)
+ this.as_operand(block, scope, expr, None, NeedsTemporary::Maybe)
}
}
use crate::build::expr::as_place::PlaceBase;
use crate::build::expr::category::{Category, RvalueFunc};
-use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
use rustc_hir::lang_items::LangItem;
use rustc_middle::middle::region;
use rustc_middle::mir::AssertKind;
})
}
ExprKind::Repeat { value, count } => {
- let value_operand =
- unpack!(block = this.as_operand(block, scope, &this.thir[value], None));
+ let value_operand = unpack!(
+ block =
+ this.as_operand(block, scope, &this.thir[value], None, NeedsTemporary::No)
+ );
block.and(Rvalue::Repeat(value_operand, count))
}
ExprKind::Binary { op, lhs, rhs } => {
- let lhs = unpack!(block = this.as_operand(block, scope, &this.thir[lhs], None));
- let rhs = unpack!(block = this.as_operand(block, scope, &this.thir[rhs], None));
+ let lhs = unpack!(
+ block =
+ this.as_operand(block, scope, &this.thir[lhs], None, NeedsTemporary::Maybe)
+ );
+ let rhs = unpack!(
+ block =
+ this.as_operand(block, scope, &this.thir[rhs], None, NeedsTemporary::No)
+ );
this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs)
}
ExprKind::Unary { op, arg } => {
- let arg = unpack!(block = this.as_operand(block, scope, &this.thir[arg], None));
+ let arg = unpack!(
+ block =
+ this.as_operand(block, scope, &this.thir[arg], None, NeedsTemporary::No)
+ );
// Check for -MIN on signed integers
if this.check_overflow && op == UnOp::Neg && expr.ty.is_signed() {
let bool_ty = this.tcx.types.bool;
block.and(Rvalue::Use(Operand::Move(Place::from(result))))
}
ExprKind::Cast { source } => {
- let source =
- unpack!(block = this.as_operand(block, scope, &this.thir[source], None));
+ let source = unpack!(
+ block =
+ this.as_operand(block, scope, &this.thir[source], None, NeedsTemporary::No)
+ );
block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty))
}
ExprKind::Pointer { cast, source } => {
- let source =
- unpack!(block = this.as_operand(block, scope, &this.thir[source], None));
+ let source = unpack!(
+ block =
+ this.as_operand(block, scope, &this.thir[source], None, NeedsTemporary::No)
+ );
block.and(Rvalue::Cast(CastKind::Pointer(cast), source, expr.ty))
}
ExprKind::Array { ref fields } => {
let fields: Vec<_> = fields
.into_iter()
.copied()
- .map(|f| unpack!(block = this.as_operand(block, scope, &this.thir[f], None)))
+ .map(|f| {
+ unpack!(
+ block = this.as_operand(
+ block,
+ scope,
+ &this.thir[f],
+ None,
+ NeedsTemporary::Maybe
+ )
+ )
+ })
.collect();
block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(el_ty)), fields))
let fields: Vec<_> = fields
.into_iter()
.copied()
- .map(|f| unpack!(block = this.as_operand(block, scope, &this.thir[f], None)))
+ .map(|f| {
+ unpack!(
+ block = this.as_operand(
+ block,
+ scope,
+ &this.thir[f],
+ None,
+ NeedsTemporary::Maybe
+ )
+ )
+ })
.collect();
block.and(Rvalue::Aggregate(Box::new(AggregateKind::Tuple), fields))
)
),
_ => {
- unpack!(block = this.as_operand(block, scope, upvar, None))
+ unpack!(
+ block = this.as_operand(
+ block,
+ scope,
+ upvar,
+ None,
+ NeedsTemporary::Maybe
+ )
+ )
}
}
}
literal: ConstantKind::zero_sized(this.tcx.types.unit),
}))))
}
- ExprKind::Yield { .. }
- | ExprKind::Literal { .. }
+
+ ExprKind::Literal { .. }
| ExprKind::NamedConst { .. }
| ExprKind::NonHirLiteral { .. }
| ExprKind::ConstParam { .. }
| ExprKind::ConstBlock { .. }
- | ExprKind::StaticRef { .. }
+ | ExprKind::StaticRef { .. } => {
+ let constant = this.as_constant(expr);
+ block.and(Rvalue::Use(Operand::Constant(Box::new(constant))))
+ }
+
+ ExprKind::Yield { .. }
| ExprKind::Block { .. }
| ExprKind::Match { .. }
| ExprKind::If { .. }
// so make an operand and then return that
debug_assert!(!matches!(
Category::of(&expr.kind),
- Some(Category::Rvalue(RvalueFunc::AsRvalue))
+ Some(Category::Rvalue(RvalueFunc::AsRvalue) | Category::Constant)
));
- let operand = unpack!(block = this.as_operand(block, scope, expr, None));
+ let operand =
+ unpack!(block = this.as_operand(block, scope, expr, None, NeedsTemporary::No));
block.and(Rvalue::Use(operand))
}
}
//! See docs in build/expr/mod.rs
use crate::build::expr::category::{Category, RvalueFunc};
-use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
+use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
use rustc_ast::InlineAsmOptions;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
block,
Some(scope),
&this.thir[f.expr],
- Some(local_info)
+ Some(local_info),
+ NeedsTemporary::Maybe,
)
),
)
ExprKind::Yield { value } => {
let scope = this.local_scope();
- let value =
- unpack!(block = this.as_operand(block, Some(scope), &this.thir[value], None));
+ let value = unpack!(
+ block = this.as_operand(
+ block,
+ Some(scope),
+ &this.thir[value],
+ None,
+ NeedsTemporary::No
+ )
+ );
let resume = this.cfg.start_new_block();
this.cfg.terminate(
block,
struct ScopeId { .. }
}
+#[derive(Debug)]
+enum NeedsTemporary {
+ /// Use this variant when whatever you are converting with `as_operand`
+ /// is the last thing you are converting. This means that if we introduced
+ /// an intermediate temporary, we'd only read it immediately after, so we can
+ /// also avoid it.
+ No,
+ /// For all cases where you aren't sure or that are too expensive to compute
+ /// for now. It is always safe to fall back to this.
+ Maybe,
+}
+
///////////////////////////////////////////////////////////////////////////
/// The `BlockAnd` "monad" packages up the new basic block along with a
/// produced value (sometimes just unit, of course). The `unpack!`
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(crate_visibility_modifier)]
+#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(let_else)]
#![feature(min_specialization)]
) -> PatKind<'tcx> {
let res = match res {
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => {
- let variant_id = self.tcx.parent(variant_ctor_id).unwrap();
+ let variant_id = self.tcx.parent(variant_ctor_id);
Res::Def(DefKind::Variant, variant_id)
}
res => res,
let mut kind = match res {
Res::Def(DefKind::Variant, variant_id) => {
- let enum_id = self.tcx.parent(variant_id).unwrap();
+ let enum_id = self.tcx.parent(variant_id);
let adt_def = self.tcx.adt_def(enum_id);
if adt_def.is_enum() {
let substs = match ty.kind() {
};
}
- let mut first = true;
- for idx in set_in_self.iter() {
- let delim = if first {
- "\u{001f}+"
- } else if f.alternate() {
- "\n\u{001f}+"
- } else {
- ", "
- };
+ fmt_diff(&set_in_self, &cleared_in_self, ctxt, f)
+ }
+}
- write!(f, "{}", delim)?;
- idx.fmt_with(ctxt, f)?;
- first = false;
- }
+impl<T, C> DebugWithContext<C> for ChunkedBitSet<T>
+where
+ T: Idx + DebugWithContext<C>,
+{
+ fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_set().entries(self.iter().map(|i| DebugWithAdapter { this: i, ctxt })).finish()
+ }
- if !f.alternate() {
- first = true;
- if !set_in_self.is_empty() && !cleared_in_self.is_empty() {
- write!(f, "\t")?;
- }
- }
+ fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let size = self.domain_size();
+ assert_eq!(size, old.domain_size());
- for idx in cleared_in_self.iter() {
- let delim = if first {
- "\u{001f}-"
- } else if f.alternate() {
- "\n\u{001f}-"
- } else {
- ", "
- };
+ let mut set_in_self = HybridBitSet::new_empty(size);
+ let mut cleared_in_self = HybridBitSet::new_empty(size);
- write!(f, "{}", delim)?;
- idx.fmt_with(ctxt, f)?;
- first = false;
+ for i in (0..size).map(T::new) {
+ match (self.contains(i), old.contains(i)) {
+ (true, false) => set_in_self.insert(i),
+ (false, true) => cleared_in_self.insert(i),
+ _ => continue,
+ };
}
- Ok(())
+ fmt_diff(&set_in_self, &cleared_in_self, ctxt, f)
}
}
-impl<T, C> DebugWithContext<C> for ChunkedBitSet<T>
+fn fmt_diff<T, C>(
+ inserted: &HybridBitSet<T>,
+ removed: &HybridBitSet<T>,
+ ctxt: &C,
+ f: &mut fmt::Formatter<'_>,
+) -> fmt::Result
where
T: Idx + DebugWithContext<C>,
{
- fn fmt_with(&self, _ctxt: &C, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!("implement when/if needed");
+ let mut first = true;
+ for idx in inserted.iter() {
+ let delim = if first {
+ "\u{001f}+"
+ } else if f.alternate() {
+ "\n\u{001f}+"
+ } else {
+ ", "
+ };
+
+ write!(f, "{}", delim)?;
+ idx.fmt_with(ctxt, f)?;
+ first = false;
+ }
+
+ if !f.alternate() {
+ first = true;
+ if !inserted.is_empty() && !removed.is_empty() {
+ write!(f, "\t")?;
+ }
}
- fn fmt_diff_with(&self, _old: &Self, _ctxt: &C, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!("implement when/if needed");
+ for idx in removed.iter() {
+ let delim = if first {
+ "\u{001f}-"
+ } else if f.alternate() {
+ "\n\u{001f}-"
+ } else {
+ ", "
+ };
+
+ write!(f, "{}", delim)?;
+ idx.fmt_with(ctxt, f)?;
+ first = false;
}
+
+ Ok(())
}
impl<T, C> DebugWithContext<C> for &'_ T
}
}
+/// Determines whether or not this LocalDecl is temp, if not it needs retagging.
+fn is_not_temp<'tcx>(local_decl: &LocalDecl<'tcx>) -> bool {
+ if let Some(local_info) = &local_decl.local_info {
+ match local_info.as_ref() {
+ LocalInfo::DerefTemp => return false,
+ _ => (),
+ };
+ }
+ return true;
+}
+
impl<'tcx> MirPass<'tcx> for AddRetag {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
sess.opts.debugging_opts.mir_emit_retag
let needs_retag = |place: &Place<'tcx>| {
// FIXME: Instead of giving up for unstable places, we should introduce
// a temporary and retag on that.
- is_stable(place.as_ref()) && may_be_reference(place.ty(&*local_decls, tcx).ty)
+ is_stable(place.as_ref())
+ && may_be_reference(place.ty(&*local_decls, tcx).ty)
+ && is_not_temp(&local_decls[place.local])
};
let place_base_raw = |place: &Place<'tcx>| {
// If this is a `Deref`, get the type of what we are deref'ing.
for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
let ty = p_ref.ty(&self.local_decls, self.tcx).ty;
- let temp =
- self.patcher.new_temp(ty, self.local_decls[p_ref.local].source_info.span);
+ let temp = self.patcher.new_local_with_info(
+ ty,
+ self.local_decls[p_ref.local].source_info.span,
+ Some(Box::new(LocalInfo::DerefTemp)),
+ );
self.patcher.add_statement(loc, StatementKind::StorageLive(temp));
// temp value, excluding projections we already covered.
let deref_place = Place::from(place_local)
.project_deeper(&p_ref.projection[last_len..], self.tcx);
+
self.patcher.add_assign(
loc,
Place::from(temp),
Rvalue::Use(Operand::Move(deref_place)),
);
-
place_local = temp;
last_len = p_ref.projection.len();
*place = temp_place;
}
- // We are destroying last temp since it's no longer used.
+ // We are destroying the previous temp since it's no longer used.
if let Some(prev_temp) = prev_temp {
self.patcher.add_statement(loc, StatementKind::StorageDead(prev_temp));
}
&add_moves_for_packed_drops::AddMovesForPackedDrops,
// `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late,
// but before optimizations begin.
+ &deref_separator::Derefer,
&add_retag::AddRetag,
&lower_intrinsics::LowerIntrinsics,
&simplify::SimplifyCfg::new("elaborate-drops"),
// `Deaggregator` is conceptually part of MIR building, some backends rely on it happening
// and it can help optimizations.
&deaggregator::Deaggregator,
- &deref_separator::Derefer,
&Lint(const_prop_lint::ConstProp),
];
cgu_def_id = None;
}
- current_def_id = tcx.parent(current_def_id).unwrap();
+ current_def_id = tcx.parent(current_def_id);
}
let cgu_def_id = cgu_def_id.unwrap();
self.parse_break_expr(attrs)
} else if self.eat_keyword(kw::Yield) {
self.parse_yield_expr(attrs)
+ } else if self.is_do_yeet() {
+ self.parse_yeet_expr(attrs)
} else if self.eat_keyword(kw::Let) {
self.parse_let_expr(attrs)
} else if self.eat_keyword(kw::Underscore) {
self.maybe_recover_from_bad_qpath(expr, true)
}
+ /// Parse `"do" "yeet" expr?`.
+ fn parse_yeet_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ let lo = self.token.span;
+
+ self.bump(); // `do`
+ self.bump(); // `yeet`
+
+ let kind = ExprKind::Yeet(self.parse_expr_opt()?);
+
+ let span = lo.to(self.prev_token.span);
+ self.sess.gated_spans.gate(sym::yeet_expr, span);
+ let expr = self.mk_expr(span, kind, attrs);
+ self.maybe_recover_from_bad_qpath(expr, true)
+ }
+
/// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten.
/// If the label is followed immediately by a `:` token, the label and `:` are
/// parsed as part of the expression (i.e. a labeled loop). The language team has
&& !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
}
+ fn is_do_yeet(&self) -> bool {
+ self.token.is_keyword(kw::Do) && self.is_keyword_ahead(1, &[kw::Yeet])
+ }
+
fn is_try_block(&self) -> bool {
self.token.is_keyword(kw::Try)
&& self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
&format!("`{}` must come before `{}`", invalid_qual, current_qual),
format!("{} {}", invalid_qual, current_qual),
Applicability::MachineApplicable,
- ).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`");
+ ).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`");
}
}
Err(err)
&format!("`{misplaced_qual}` must come before `{current_qual}`"),
format!("{misplaced_qual} {current_qual}"),
Applicability::MachineApplicable,
- ).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`");
+ ).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`");
}
}
// Recover incorrect visibility order such as `async pub`
use rustc_ast::AttrId;
use rustc_ast::DUMMY_NODE_ID;
use rustc_ast::{self as ast, AnonConst, AstLike, AttrStyle, AttrVec, Const, CrateSugar, Extern};
-use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacDelimiter, Mutability, StrLit, Unsafe};
-use rustc_ast::{Visibility, VisibilityKind};
+use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacArgsEq, MacDelimiter, Mutability, StrLit};
+use rustc_ast::{Unsafe, Visibility, VisibilityKind};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::sync::Lrc;
use rustc_errors::PResult;
use rustc_errors::{
struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, MultiSpan,
} else if !delimited_only {
if self.eat(&token::Eq) {
let eq_span = self.prev_token.span;
-
- // Collect tokens because they are used during lowering to HIR.
- let expr = self.parse_expr_force_collect()?;
- let span = expr.span;
-
- let token_kind = token::Interpolated(Lrc::new(token::NtExpr(expr)));
- MacArgs::Eq(eq_span, Token::new(token_kind, span))
+ MacArgs::Eq(eq_span, MacArgsEq::Ast(self.parse_expr_force_collect()?))
} else {
MacArgs::Empty
}
use crate::parse_in;
-use rustc_ast::tokenstream::{DelimSpan, TokenTree};
-use rustc_ast::{self as ast, Attribute, MacArgs, MacDelimiter, MetaItem, MetaItemKind};
+use rustc_ast::tokenstream::DelimSpan;
+use rustc_ast::{self as ast, Attribute, MacArgs, MacArgsEq, MacDelimiter, MetaItem, MetaItemKind};
+use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, FatalError, PResult};
use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
path: item.path.clone(),
kind: match &item.args {
MacArgs::Empty => MetaItemKind::Word,
- MacArgs::Eq(_, t) => {
- let t = TokenTree::Token(t.clone()).into();
- let v = parse_in(sess, t, "name value", |p| p.parse_unsuffixed_lit())?;
- MetaItemKind::NameValue(v)
- }
MacArgs::Delimited(dspan, delim, t) => {
check_meta_bad_delim(sess, *dspan, *delim, "wrong meta list delimiters");
let nmis = parse_in(sess, t.clone(), "meta list", |p| p.parse_meta_seq_top())?;
MetaItemKind::List(nmis)
}
+ MacArgs::Eq(_, MacArgsEq::Ast(expr)) => {
+ if let ast::ExprKind::Lit(lit) = &expr.kind {
+ if !lit.kind.is_unsuffixed() {
+ let mut err = sess.span_diagnostic.struct_span_err(
+ lit.span,
+ "suffixed literals are not allowed in attributes",
+ );
+ err.help(
+ "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
+ use an unsuffixed version (`1`, `1.0`, etc.)",
+ );
+ return Err(err);
+ } else {
+ MetaItemKind::NameValue(lit.clone())
+ }
+ } else {
+ // The non-error case can happen with e.g. `#[foo = 1+1]`. The error case can
+ // happen with e.g. `#[foo = include_str!("non-existent-file.rs")]`; in that
+ // case we delay the error because an earlier error will have already been
+ // reported.
+ let msg = format!("unexpected expression: `{}`", pprust::expr_to_string(expr));
+ let mut err = sess.span_diagnostic.struct_span_err(expr.span, msg);
+ if let ast::ExprKind::Err = expr.kind {
+ err.downgrade_to_delayed_bug();
+ }
+ return Err(err);
+ }
+ }
+ MacArgs::Eq(_, MacArgsEq::Hir(lit)) => MetaItemKind::NameValue(lit.clone()),
},
})
}
edition = "2021"
[dependencies]
-rustc_span = { path = "../rustc_span" }
rustc_lexer = { path = "../rustc_lexer" }
html_playground_url = "https://play.rust-lang.org/",
test(attr(deny(warnings)))
)]
-#![feature(nll)]
-#![feature(bool_to_option)]
+// We want to be able to build this crate with a stable compiler, so no
+// `#![feature]` attributes should be added.
pub use Alignment::*;
pub use Count::*;
use std::str;
use std::string;
-use rustc_span::{InnerSpan, Symbol};
+// Note: copied from rustc_span
+/// Range inside of a `Span` used for diagnostics when we only have access to relative positions.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct InnerSpan {
+ pub start: usize,
+ pub end: usize,
+}
+
+impl InnerSpan {
+ pub fn new(start: usize, end: usize) -> InnerSpan {
+ InnerSpan { start, end }
+ }
+}
/// The type of format string that we are parsing.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Argument<'a> {
/// Where to find this argument
- pub position: Position,
+ pub position: Position<'a>,
/// How to format the argument
pub format: FormatSpec<'a>,
}
/// Packed version of various flags provided.
pub flags: u32,
/// The integer precision to use.
- pub precision: Count,
+ pub precision: Count<'a>,
/// The span of the precision formatting flag (for diagnostics).
pub precision_span: Option<InnerSpan>,
/// The string width requested for the resulting format.
- pub width: Count,
+ pub width: Count<'a>,
/// The span of the width formatting flag (for diagnostics).
pub width_span: Option<InnerSpan>,
/// The descriptor string representing the name of the format desired for
/// Enum describing where an argument for a format can be located.
#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum Position {
+pub enum Position<'a> {
/// The argument is implied to be located at an index
ArgumentImplicitlyIs(usize),
/// The argument is located at a specific index given in the format
ArgumentIs(usize),
/// The argument has a name.
- ArgumentNamed(Symbol, InnerSpan),
+ ArgumentNamed(&'a str, InnerSpan),
}
-impl Position {
+impl Position<'_> {
pub fn index(&self) -> Option<usize> {
match self {
ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i),
/// A count is used for the precision and width parameters of an integer, and
/// can reference either an argument or a literal integer.
#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum Count {
+pub enum Count<'a> {
/// The count is specified explicitly.
CountIs(usize),
/// The count is specified by the argument with the given name.
- CountIsName(Symbol, InnerSpan),
+ CountIsName(&'a str, InnerSpan),
/// The count is specified by the argument at the given index.
CountIsParam(usize),
/// The count is implied and cannot be explicitly specified.
/// integer index of an argument, a named argument, or a blank string.
/// Returns `Some(parsed_position)` if the position is not implicitly
/// consuming a macro argument, `None` if it's the case.
- fn position(&mut self) -> Option<Position> {
+ fn position(&mut self) -> Option<Position<'a>> {
if let Some(i) = self.integer() {
Some(ArgumentIs(i))
} else {
let word = self.word();
let end = start + word.len();
let span = self.to_span_index(start).to(self.to_span_index(end));
- Some(ArgumentNamed(Symbol::intern(word), span))
+ Some(ArgumentNamed(word, span))
}
// This is an `ArgumentNext`.
/// Parses a `Count` parameter at the current position. This does not check
/// for 'CountIsNextParam' because that is only used in precision, not
/// width.
- fn count(&mut self, start: usize) -> (Count, Option<InnerSpan>) {
+ fn count(&mut self, start: usize) -> (Count<'a>, Option<InnerSpan>) {
if let Some(i) = self.integer() {
if let Some(end) = self.consume_pos('$') {
let span = self.to_span_index(start).to(self.to_span_index(end + 1));
(CountImplied, None)
} else if let Some(end) = self.consume_pos('$') {
let span = self.to_span_index(start + 1).to(self.to_span_index(end));
- (CountIsName(Symbol::intern(word), span), None)
+ (CountIsName(word, span), None)
} else {
self.cur = tmp;
(CountImplied, None)
break;
}
}
- found.then_some(cur)
+ if found { Some(cur) } else { None }
}
}
}
#[test]
fn format_counts() {
- rustc_span::create_default_session_globals_then(|| {
- same(
- "{:10x}",
- &[NextArgument(Argument {
- position: ArgumentImplicitlyIs(0),
- format: FormatSpec {
- fill: None,
- align: AlignUnknown,
- flags: 0,
- precision: CountImplied,
- width: CountIs(10),
- precision_span: None,
- width_span: None,
- ty: "x",
- ty_span: None,
- },
- })],
- );
- same(
- "{:10$.10x}",
- &[NextArgument(Argument {
- position: ArgumentImplicitlyIs(0),
- format: FormatSpec {
- fill: None,
- align: AlignUnknown,
- flags: 0,
- precision: CountIs(10),
- width: CountIsParam(10),
- precision_span: None,
- width_span: Some(InnerSpan::new(3, 6)),
- ty: "x",
- ty_span: None,
- },
- })],
- );
- same(
- "{:.*x}",
- &[NextArgument(Argument {
- position: ArgumentImplicitlyIs(1),
- format: FormatSpec {
- fill: None,
- align: AlignUnknown,
- flags: 0,
- precision: CountIsParam(0),
- width: CountImplied,
- precision_span: Some(InnerSpan::new(3, 5)),
- width_span: None,
- ty: "x",
- ty_span: None,
- },
- })],
- );
- same(
- "{:.10$x}",
- &[NextArgument(Argument {
- position: ArgumentImplicitlyIs(0),
- format: FormatSpec {
- fill: None,
- align: AlignUnknown,
- flags: 0,
- precision: CountIsParam(10),
- width: CountImplied,
- precision_span: Some(InnerSpan::new(3, 7)),
- width_span: None,
- ty: "x",
- ty_span: None,
- },
- })],
- );
- same(
- "{:a$.b$?}",
- &[NextArgument(Argument {
- position: ArgumentImplicitlyIs(0),
- format: FormatSpec {
- fill: None,
- align: AlignUnknown,
- flags: 0,
- precision: CountIsName(Symbol::intern("b"), InnerSpan::new(6, 7)),
- width: CountIsName(Symbol::intern("a"), InnerSpan::new(4, 4)),
- precision_span: None,
- width_span: None,
- ty: "?",
- ty_span: None,
- },
- })],
- );
- });
+ same(
+ "{:10x}",
+ &[NextArgument(Argument {
+ position: ArgumentImplicitlyIs(0),
+ format: FormatSpec {
+ fill: None,
+ align: AlignUnknown,
+ flags: 0,
+ precision: CountImplied,
+ width: CountIs(10),
+ precision_span: None,
+ width_span: None,
+ ty: "x",
+ ty_span: None,
+ },
+ })],
+ );
+ same(
+ "{:10$.10x}",
+ &[NextArgument(Argument {
+ position: ArgumentImplicitlyIs(0),
+ format: FormatSpec {
+ fill: None,
+ align: AlignUnknown,
+ flags: 0,
+ precision: CountIs(10),
+ width: CountIsParam(10),
+ precision_span: None,
+ width_span: Some(InnerSpan::new(3, 6)),
+ ty: "x",
+ ty_span: None,
+ },
+ })],
+ );
+ same(
+ "{:.*x}",
+ &[NextArgument(Argument {
+ position: ArgumentImplicitlyIs(1),
+ format: FormatSpec {
+ fill: None,
+ align: AlignUnknown,
+ flags: 0,
+ precision: CountIsParam(0),
+ width: CountImplied,
+ precision_span: Some(InnerSpan::new(3, 5)),
+ width_span: None,
+ ty: "x",
+ ty_span: None,
+ },
+ })],
+ );
+ same(
+ "{:.10$x}",
+ &[NextArgument(Argument {
+ position: ArgumentImplicitlyIs(0),
+ format: FormatSpec {
+ fill: None,
+ align: AlignUnknown,
+ flags: 0,
+ precision: CountIsParam(10),
+ width: CountImplied,
+ precision_span: Some(InnerSpan::new(3, 7)),
+ width_span: None,
+ ty: "x",
+ ty_span: None,
+ },
+ })],
+ );
+ same(
+ "{:a$.b$?}",
+ &[NextArgument(Argument {
+ position: ArgumentImplicitlyIs(0),
+ format: FormatSpec {
+ fill: None,
+ align: AlignUnknown,
+ flags: 0,
+ precision: CountIsName("b", InnerSpan::new(6, 7)),
+ width: CountIsName("a", InnerSpan::new(4, 4)),
+ precision_span: None,
+ width_span: None,
+ ty: "?",
+ ty_span: None,
+ },
+ })],
+ );
}
#[test]
fn format_flags() {
_ if self.in_pat => {}
Res::PrimTy(..) | Res::SelfCtor(..) | Res::Local(..) => {}
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), ctor_def_id) => {
- let variant_id = self.tcx.parent(ctor_def_id).unwrap();
- let enum_id = self.tcx.parent(variant_id).unwrap();
+ let variant_id = self.tcx.parent(ctor_def_id);
+ let enum_id = self.tcx.parent(variant_id);
self.check_def_id(enum_id);
if !self.ignore_variant_stack.contains(&ctor_def_id) {
self.check_def_id(variant_id);
}
}
Res::Def(DefKind::Variant, variant_id) => {
- let enum_id = self.tcx.parent(variant_id).unwrap();
+ let enum_id = self.tcx.parent(variant_id);
self.check_def_id(enum_id);
if !self.ignore_variant_stack.contains(&variant_id) {
self.check_def_id(variant_id);
impl<'tcx> ItemLikeVisitor<'tcx> for EntryContext<'tcx> {
fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
- let at_root = self.tcx.local_parent(item.def_id) == Some(CRATE_DEF_ID);
+ let at_root = self.tcx.opt_local_parent(item.def_id) == Some(CRATE_DEF_ID);
find_item(item, self, at_root);
}
let def_id = local_def_id.to_def_id();
// Don't run unused pass for #[derive()]
- if let Some(parent) = self.tcx.parent(def_id)
- && let DefKind::Impl = self.tcx.def_kind(parent.expect_local())
- && self.tcx.has_attr(parent, sym::automatically_derived)
+ let parent = self.tcx.local_parent(local_def_id);
+ if let DefKind::Impl = self.tcx.def_kind(parent)
+ && self.tcx.has_attr(parent.to_def_id(), sym::automatically_derived)
{
return;
}
self.visit_nested_body(body);
}
hir::ImplItemKind::Fn(_, body) => {
- let impl_def_id =
- self.tcx.parent(search_item.to_def_id()).unwrap().expect_local();
+ let impl_def_id = self.tcx.local_parent(search_item);
if method_might_be_inlined(self.tcx, impl_item, impl_def_id) {
self.visit_nested_body(body)
}
use rustc_middle::ty::fold::TypeVisitor;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::InternalSubsts;
-use rustc_middle::ty::{self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind};
+use rustc_middle::ty::{TraitRef, Ty, TyCtxt, TypeFoldable};
use rustc_session::lint;
use rustc_span::hygiene::Transparency;
use rustc_span::symbol::{kw, Ident};
return;
}
- let item_def_id = local_def_id.to_def_id();
- let macro_module_def_id =
- ty::DefIdTree::parent(self.tcx, item_def_id).unwrap().expect_local();
+ let macro_module_def_id = self.tcx.local_parent(local_def_id);
if self.tcx.hir().opt_def_kind(macro_module_def_id) != Some(DefKind::Mod) {
// The macro's parent doesn't correspond to a `mod`, return early (#63164, #65252).
return;
if changed_reachability || module_def_id == CRATE_DEF_ID {
break;
}
- module_def_id =
- ty::DefIdTree::parent(self.tcx, module_def_id.to_def_id()).unwrap().expect_local();
+ module_def_id = self.tcx.local_parent(module_def_id);
}
}
&segments,
Some(TypeNS),
parent_scope,
- if finalize { Finalize::SimplePath(id, path.span) } else { Finalize::No },
+ finalize.then(|| Finalize::new(id, path.span)),
None,
) {
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
self.insert_unused_macro(ident, def_id, item.id);
}
self.r.visibilities.insert(def_id, vis);
- self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
+ let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding {
parent_macro_rules_scope: parent_scope.macro_rules,
binding,
ident,
}),
- ))
+ ));
+ self.r.macro_rules_scopes.insert(def_id, scope);
+ scope
} else {
let module = parent_scope.module;
let vis = match item.kind {
&& self
.r
.trait_impl_items
- .contains(&ty::DefIdTree::parent(&*self.r, def_id).unwrap().expect_local()))
+ .contains(&ty::DefIdTree::local_parent(&*self.r, local_def_id)))
{
// Trait impl item visibility is inherited from its trait when not specified
// explicitly. In that case we cannot determine it here in early resolve,
}
fn report_with_use_injections(&mut self, krate: &Crate) {
- for UseError { mut err, candidates, def_id, instead, suggestion } in
+ for UseError { mut err, candidates, def_id, instead, suggestion, path } in
self.use_injections.drain(..)
{
let (span, found_use) = if let Some(def_id) = def_id.as_local() {
if instead { Instead::Yes } else { Instead::No },
found_use,
IsPattern::No,
+ path,
);
} else if let Some((span, msg, sugg, appl)) = suggestion {
err.span_suggestion(span, msg, sugg, appl);
crate fn lint_if_path_starts_with_module(
&mut self,
- finalize: Finalize,
+ finalize: Option<Finalize>,
path: &[Segment],
second_binding: Option<&NameBinding<'_>>,
) {
- let (diag_id, diag_span) = match finalize {
- Finalize::No => return,
- Finalize::SimplePath(id, path_span) => (id, path_span),
- Finalize::UsePath { root_id, root_span, .. } => (root_id, root_span),
- Finalize::QPathTrait { qpath_id, qpath_span, .. } => (qpath_id, qpath_span),
+ let Some(Finalize { node_id, root_span, .. }) = finalize else {
+ return;
};
let first_name = match path.get(0) {
}
}
- let diag = BuiltinLintDiagnostics::AbsPathWithModule(diag_span);
+ let diag = BuiltinLintDiagnostics::AbsPathWithModule(root_span);
self.lint_buffer.buffer_lint_with_diagnostic(
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
- diag_id,
- diag_span,
+ node_id,
+ root_span,
"absolute paths must start with `self`, `super`, \
`crate`, or an external crate name in the 2018 edition",
diag,
Instead::No,
FoundUse::Yes,
IsPattern::Yes,
+ vec![],
);
}
err
segms.push(ast::PathSegment::from_ident(ident));
let path = Path { span: name_binding.span, segments: segms, tokens: None };
let did = match res {
- Res::Def(DefKind::Ctor(..), did) => this.parent(did),
+ Res::Def(DefKind::Ctor(..), did) => this.opt_parent(did),
_ => res.opt_def_id(),
};
Instead::No,
FoundUse::Yes,
IsPattern::No,
+ vec![],
);
if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
&parent_scope,
None,
false,
- false,
None,
) {
let desc = match binding.res() {
_,
) = binding.kind
{
- let def_id = self.parent(ctor_def_id).expect("no parent for a constructor");
+ let def_id = self.parent(ctor_def_id);
let fields = self.field_names.get(&def_id)?;
return fields.iter().map(|name| name.span).reduce(Span::to); // None for `struct Foo()`
}
opt_ns: Option<Namespace>, // `None` indicates a module path in import
parent_scope: &ParentScope<'a>,
ribs: Option<&PerNS<Vec<Rib<'a>>>>,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
module: Option<ModuleOrUniformRoot<'a>>,
i: usize,
ident: Ident,
ns_to_try,
parent_scope,
None,
- false,
- unusable_binding,
+ ignore_binding,
).ok()
} else if let Some(ribs) = ribs
&& let Some(TypeNS | ValueNS) = opt_ns
ident,
ns_to_try,
parent_scope,
- Finalize::No,
+ None,
&ribs[ns_to_try],
- unusable_binding,
+ ignore_binding,
) {
// we found a locally-imported or available item/module
Some(LexicalScopeBinding::Item(binding)) => Some(binding),
parent_scope,
None,
false,
- false,
- unusable_binding,
+ ignore_binding,
).ok()
};
if let Some(binding) = binding {
ident,
ValueNS,
parent_scope,
- Finalize::No,
+ None,
&ribs[ValueNS],
- unusable_binding,
+ ignore_binding,
)
} else {
None
instead: Instead,
found_use: FoundUse,
is_pattern: IsPattern,
+ path: Vec<Segment>,
) {
if candidates.is_empty() {
return;
accessible_path_strings.into_iter().map(|a| a.0),
Applicability::MaybeIncorrect,
);
+ if let [first, .., last] = &path[..] {
+ err.span_suggestion_verbose(
+ first.ident.span.until(last.ident.span),
+ &format!("if you import `{}`, refer to it directly", last.ident),
+ String::new(),
+ Applicability::Unspecified,
+ );
+ }
} else {
msg.push(':');
mut ident: Ident,
ns: Namespace,
parent_scope: &ParentScope<'a>,
- finalize_full: Finalize,
+ finalize: Option<Finalize>,
ribs: &[Rib<'a>],
- unusable_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Option<LexicalScopeBinding<'a>> {
assert!(ns == TypeNS || ns == ValueNS);
let orig_ident = ident;
let normalized_ident = Ident { span: normalized_span, ..ident };
// Walk backwards up the ribs in scope.
- let finalize = finalize_full.path_span();
let mut module = self.graph_root;
for i in (0..ribs.len()).rev() {
debug!("walk rib\n{:?}", ribs[i].bindings);
i,
rib_ident,
*res,
- finalize,
+ finalize.map(|finalize| finalize.path_span),
*original_rib_ident_def,
ribs,
)));
ns,
parent_scope,
finalize,
- false,
- unusable_binding,
+ ignore_binding,
);
if let Ok(binding) = item {
// The ident resolves to an item.
}
self.early_resolve_ident_in_lexical_scope(
orig_ident,
- ScopeSet::Late(ns, module, finalize_full.node_id()),
+ ScopeSet::Late(ns, module, finalize.map(|finalize| finalize.node_id)),
parent_scope,
finalize,
finalize.is_some(),
- false,
- unusable_binding,
+ ignore_binding,
)
.ok()
.map(LexicalScopeBinding::Item)
orig_ident: Ident,
scope_set: ScopeSet<'a>,
parent_scope: &ParentScope<'a>,
- finalize: Option<Span>,
+ finalize: Option<Finalize>,
force: bool,
- last_import_segment: bool,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Result<&'a NameBinding<'a>, Determinacy> {
bitflags::bitflags! {
struct Flags: u8 {
ns,
parent_scope,
finalize,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
);
match binding {
Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)),
adjusted_parent_scope,
!matches!(scope_set, ScopeSet::Late(..)),
finalize,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
);
match binding {
Ok(binding) => {
ns,
parent_scope,
None,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
) {
if use_prelude || this.is_builtin_macro(binding.res()) {
result = Ok((binding, Flags::MISC_FROM_PRELUDE));
ns: Namespace,
parent_scope: &ParentScope<'a>,
) -> Result<&'a NameBinding<'a>, Determinacy> {
- self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, false, None)
+ self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, None)
.map_err(|(determinacy, _)| determinacy)
}
ident: Ident,
ns: Namespace,
parent_scope: &ParentScope<'a>,
- finalize: Option<Span>,
- // We are resolving a last import segment during import validation.
- last_import_segment: bool,
- // This binding should be ignored during in-module resolution, so that we don't get
- // "self-confirming" import resolutions during import validation.
- unusable_binding: Option<&'a NameBinding<'a>>,
+ finalize: Option<Finalize>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Result<&'a NameBinding<'a>, Determinacy> {
- self.resolve_ident_in_module_ext(
- module,
- ident,
- ns,
- parent_scope,
- finalize,
- last_import_segment,
- unusable_binding,
- )
- .map_err(|(determinacy, _)| determinacy)
+ self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize, ignore_binding)
+ .map_err(|(determinacy, _)| determinacy)
}
#[tracing::instrument(level = "debug", skip(self))]
mut ident: Ident,
ns: Namespace,
parent_scope: &ParentScope<'a>,
- finalize: Option<Span>,
- last_import_segment: bool,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ finalize: Option<Finalize>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
let tmp_parent_scope;
let mut adjusted_parent_scope = parent_scope;
adjusted_parent_scope,
false,
finalize,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
)
}
ident: Ident,
ns: Namespace,
parent_scope: &ParentScope<'a>,
- finalize: Option<Span>,
- last_import_segment: bool,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ finalize: Option<Finalize>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Result<&'a NameBinding<'a>, Determinacy> {
self.resolve_ident_in_module_unadjusted_ext(
module,
parent_scope,
false,
finalize,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
)
.map_err(|(determinacy, _)| determinacy)
}
ns: Namespace,
parent_scope: &ParentScope<'a>,
restricted_shadowing: bool,
- finalize: Option<Span>,
- last_import_segment: bool,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ finalize: Option<Finalize>,
+ // This binding should be ignored during in-module resolution, so that we don't get
+ // "self-confirming" import resolutions during import validation and checking.
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
let module = match module {
ModuleOrUniformRoot::Module(module) => module,
parent_scope,
finalize,
finalize.is_some(),
- last_import_segment,
- unusable_binding,
+ ignore_binding,
);
return binding.map_err(|determinacy| (determinacy, Weak::No));
}
parent_scope,
finalize,
finalize.is_some(),
- last_import_segment,
- unusable_binding,
+ ignore_binding,
);
return binding.map_err(|determinacy| (determinacy, Weak::No));
}
let resolution =
self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports.
- if let Some(path_span) = finalize {
+ if let Some(Finalize { path_span, report_private, .. }) = finalize {
// If the primary binding is unusable, search further and return the shadowed glob
// binding if it exists. What we really want here is having two separate scopes in
// a module - one for non-globs and one for globs, but until that's done use this
// hack to avoid inconsistent resolution ICEs during import validation.
let binding = [resolution.binding, resolution.shadowed_glob]
.into_iter()
- .filter_map(|binding| match (binding, unusable_binding) {
- (Some(binding), Some(unusable_binding))
- if ptr::eq(binding, unusable_binding) =>
- {
- None
- }
+ .filter_map(|binding| match (binding, ignore_binding) {
+ (Some(binding), Some(ignored)) if ptr::eq(binding, ignored) => None,
_ => binding,
})
.next();
};
if !self.is_accessible_from(binding.vis, parent_scope.module) {
- if last_import_segment {
- return Err((Determined, Weak::No));
- } else {
+ if report_private {
self.privacy_errors.push(PrivacyError {
ident,
binding,
dedup_span: path_span,
});
+ } else {
+ return Err((Determined, Weak::No));
}
}
}
let check_usable = |this: &mut Self, binding: &'a NameBinding<'a>| {
- if let Some(unusable_binding) = unusable_binding {
- if ptr::eq(binding, unusable_binding) {
- return Err((Determined, Weak::No));
- }
+ if let Some(ignored) = ignore_binding && ptr::eq(binding, ignored) {
+ return Err((Determined, Weak::No));
}
let usable = this.is_accessible_from(binding.vis, parent_scope.module);
if usable { Ok(binding) } else { Err((Determined, Weak::No)) }
ns,
&single_import.parent_scope,
None,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
) {
Err(Determined) => continue,
Ok(binding)
ns,
adjusted_parent_scope,
None,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
);
match result {
ConstantItemRibKind(trivial, _) => {
let features = self.session.features_untracked();
// HACK(min_const_generics): We currently only allow `N` or `{ N }`.
- if !(trivial || features.generic_const_exprs) {
+ if !(trivial == HasGenericParams::Yes || features.generic_const_exprs) {
// HACK(min_const_generics): If we encounter `Self` in an anonymous constant
// we can't easily tell if it's generic at this stage, so we instead remember
// this and then enforce the self type to be concrete later on.
ConstantItemRibKind(trivial, _) => {
let features = self.session.features_untracked();
// HACK(min_const_generics): We currently only allow `N` or `{ N }`.
- if !(trivial || features.generic_const_exprs) {
+ if !(trivial == HasGenericParams::Yes || features.generic_const_exprs) {
if let Some(span) = finalize {
self.report_error(
span,
opt_ns: Option<Namespace>, // `None` indicates a module path in import
parent_scope: &ParentScope<'a>,
) -> PathResult<'a> {
- self.resolve_path_with_ribs(path, opt_ns, parent_scope, Finalize::No, None, None)
+ self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None)
}
#[tracing::instrument(level = "debug", skip(self))]
path: &[Segment],
opt_ns: Option<Namespace>, // `None` indicates a module path in import
parent_scope: &ParentScope<'a>,
- finalize: Finalize,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ finalize: Option<Finalize>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> PathResult<'a> {
- self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, unusable_binding)
+ self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, ignore_binding)
}
crate fn resolve_path_with_ribs(
path: &[Segment],
opt_ns: Option<Namespace>, // `None` indicates a module path in import
parent_scope: &ParentScope<'a>,
- finalize_full: Finalize,
+ finalize: Option<Finalize>,
ribs: Option<&PerNS<Vec<Rib<'a>>>>,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> PathResult<'a> {
- debug!("resolve_path(path={:?}, opt_ns={:?}, finalize={:?})", path, opt_ns, finalize_full);
+ debug!("resolve_path(path={:?}, opt_ns={:?}, finalize={:?})", path, opt_ns, finalize);
- let finalize = finalize_full.path_span();
let mut module = None;
let mut allow_super = true;
let mut second_binding = None;
ns,
parent_scope,
finalize,
- false,
- unusable_binding,
+ ignore_binding,
)
} else if let Some(ribs) = ribs
&& let Some(TypeNS | ValueNS) = opt_ns
ident,
ns,
parent_scope,
- finalize_full,
+ finalize,
&ribs[ns],
- unusable_binding,
+ ignore_binding,
) {
// we found a locally-imported or available item/module
Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
parent_scope,
finalize,
finalize.is_some(),
- false,
- unusable_binding,
+ ignore_binding,
)
};
FindBindingResult::Binding(binding)
} else if res == Res::Err {
return PathResult::NonModule(PartialRes::new(Res::Err));
} else if opt_ns.is_some() && (is_last || maybe_assoc) {
- self.lint_if_path_starts_with_module(finalize_full, path, second_binding);
+ self.lint_if_path_starts_with_module(finalize, path, second_binding);
return PathResult::NonModule(PartialRes::with_unresolved_segments(
res,
path.len() - i - 1,
opt_ns,
parent_scope,
ribs,
- unusable_binding,
+ ignore_binding,
module,
i,
ident,
}
}
- self.lint_if_path_starts_with_module(finalize_full, path, second_binding);
+ self.lint_if_path_starts_with_module(finalize, path, second_binding);
PathResult::Module(match module {
Some(module) => module,
use rustc_data_structures::intern::Interned;
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
use rustc_hir::def::{self, PartialRes};
-use rustc_hir::def_id::DefId;
use rustc_middle::metadata::ModChild;
use rustc_middle::span_bug;
use rustc_middle::ty;
pub r: &'a mut Resolver<'b>,
}
-impl<'a, 'b> ty::DefIdTree for &'a ImportResolver<'a, 'b> {
- fn parent(self, id: DefId) -> Option<DefId> {
- self.r.parent(id)
- }
-}
-
impl<'a, 'b> ImportResolver<'a, 'b> {
// Import resolution
//
ns,
&import.parent_scope,
None,
- false,
None,
);
import.vis.set(orig_vis);
/// consolidate multiple unresolved import errors into a single diagnostic.
fn finalize_import(&mut self, import: &'b Import<'b>) -> Option<UnresolvedImportError> {
let orig_vis = import.vis.replace(ty::Visibility::Invisible);
- let unusable_binding = match &import.kind {
+ let ignore_binding = match &import.kind {
ImportKind::Single { target_bindings, .. } => target_bindings[TypeNS].get(),
_ => None,
};
let prev_ambiguity_errors_len = self.r.ambiguity_errors.len();
- let finalize = Finalize::UsePath {
- root_id: import.root_id,
- root_span: import.root_span,
- path_span: import.span,
- };
+ let finalize = Finalize::with_root_span(import.root_id, import.span, import.root_span);
let path_res = self.r.resolve_path(
&import.module_path,
None,
&import.parent_scope,
- finalize,
- unusable_binding,
+ Some(finalize),
+ ignore_binding,
);
let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len;
import.vis.set(orig_vis);
// 2 segments, so the `resolve_path` above won't trigger it.
let mut full_path = import.module_path.clone();
full_path.push(Segment::from_ident(Ident::empty()));
- self.r.lint_if_path_starts_with_module(finalize, &full_path, None);
+ self.r.lint_if_path_starts_with_module(Some(finalize), &full_path, None);
}
if let ModuleOrUniformRoot::Module(module) = module {
}
if !is_prelude &&
max_vis.get() != ty::Visibility::Invisible && // Allow empty globs.
- !max_vis.get().is_at_least(import.vis.get(), &*self)
+ !max_vis.get().is_at_least(import.vis.get(), &*self.r)
{
let msg = "glob import doesn't reexport anything because no candidate is public enough";
self.r.lint_buffer.buffer_lint(UNUSED_IMPORTS, import.id, import.span, msg);
ident,
ns,
&import.parent_scope,
- Some(import.span),
- true,
+ Some(Finalize { report_private: false, ..finalize }),
target_bindings[ns].get(),
);
import.vis.set(orig_vis);
ident,
ns,
&import.parent_scope,
- Some(import.span),
- false,
+ Some(finalize),
None,
);
if binding.is_ok() {
full_path.push(Segment::from_ident(ident));
self.r.per_ns(|this, ns| {
if let Ok(binding) = source_bindings[ns].get() {
- this.lint_if_path_starts_with_module(finalize, &full_path, Some(binding));
+ this.lint_if_path_starts_with_module(Some(finalize), &full_path, Some(binding));
}
});
}
&import.parent_scope,
None,
false,
- false,
target_bindings[ns].get(),
) {
Ok(other_binding) => {
No,
}
+impl HasGenericParams {
+ fn force_yes_if(self, b: bool) -> Self {
+ if b { Self::Yes } else { self }
+ }
+}
+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
crate enum ConstantItemKind {
Const,
/// We're in a constant item. Can't refer to dynamic stuff.
///
- /// The `bool` indicates if this constant may reference generic parameters
- /// and is used to only allow generic parameters to be used in trivial constant expressions.
- ConstantItemRibKind(bool, Option<(Ident, ConstantItemKind)>),
+ /// The item may reference generic parameters in trivial constant expressions.
+ /// All other constants aren't allowed to use generic params at all.
+ ConstantItemRibKind(HasGenericParams, Option<(Ident, ConstantItemKind)>),
/// We passed through a module.
ModuleRibKind(Module<'a>),
) | Res::Local(..)
| Res::SelfCtor(..)
),
- PathSource::Pat => matches!(
- res,
- Res::Def(
- DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst,
- _,
- ) | Res::SelfCtor(..)
- ),
+ PathSource::Pat => {
+ res.expected_in_unit_struct_pat()
+ || matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _))
+ }
PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(),
PathSource::Struct => matches!(
res,
.resolve_ident_in_lexical_scope(
self_ty,
TypeNS,
- Finalize::SimplePath(ty.id, ty.span),
+ Some(Finalize::new(ty.id, ty.span)),
None,
)
.map_or(Res::Err, |d| d.res());
// Note that we might not be inside of an repeat expression here,
// but considering that `IsRepeatExpr` is only relevant for
// non-trivial constants this is doesn't matter.
- self.with_constant_rib(IsRepeatExpr::No, true, None, |this| {
- this.smart_resolve_path(
- ty.id,
- qself.as_ref(),
- path,
- PathSource::Expr(None),
- );
-
- if let Some(ref qself) = *qself {
- this.visit_ty(&qself.ty);
- }
- this.visit_path(path, ty.id);
- });
+ self.with_constant_rib(
+ IsRepeatExpr::No,
+ HasGenericParams::Yes,
+ None,
+ |this| {
+ this.smart_resolve_path(
+ ty.id,
+ qself.as_ref(),
+ path,
+ PathSource::Expr(None),
+ );
+
+ if let Some(ref qself) = *qself {
+ this.visit_ty(&qself.ty);
+ }
+ this.visit_path(path, ty.id);
+ },
+ );
self.diagnostic_metadata.currently_processing_generics = prev;
return;
ident,
ns,
&self.parent_scope,
- Finalize::No,
+ None,
&self.ribs[ns],
None,
)
&mut self,
ident: Ident,
ns: Namespace,
- finalize: Finalize,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ finalize: Option<Finalize>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Option<LexicalScopeBinding<'a>> {
self.r.resolve_ident_in_lexical_scope(
ident,
&self.parent_scope,
finalize,
&self.ribs[ns],
- unusable_binding,
+ ignore_binding,
)
}
&mut self,
path: &[Segment],
opt_ns: Option<Namespace>, // `None` indicates a module path in import
- finalize: Finalize,
+ finalize: Option<Finalize>,
) -> PathResult<'a> {
self.r.resolve_path_with_ribs(
path,
partial_res: PartialRes,
path: &[Segment],
source: PathSource<'_>,
- finalize: Finalize,
+ path_span: Span,
) {
- let Some(path_span) = finalize.path_span() else {
- return;
- };
let proj_start = path.len() - partial_res.unresolved_segments();
for (i, segment) in path.iter().enumerate() {
if segment.has_lifetime_args {
// Figure out if this is a type/trait segment,
// which may need lifetime elision performed.
let type_def_id = match partial_res.base_res() {
- Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => {
- self.r.parent(def_id).unwrap()
- }
- Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => {
- self.r.parent(def_id).unwrap()
- }
+ Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => self.r.parent(def_id),
+ Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => self.r.parent(def_id),
Res::Def(DefKind::Struct, def_id)
| Res::Def(DefKind::Union, def_id)
| Res::Def(DefKind::Enum, def_id)
report_error(self, ns);
}
Some(LexicalScopeBinding::Item(binding)) => {
- if let Some(LexicalScopeBinding::Res(..)) = self
- .resolve_ident_in_lexical_scope(ident, ns, Finalize::No, Some(binding))
+ if let Some(LexicalScopeBinding::Res(..)) =
+ self.resolve_ident_in_lexical_scope(ident, ns, None, Some(binding))
{
report_error(self, ns);
}
// not used as part of the type system, this is far less surprising.
this.with_constant_rib(
IsRepeatExpr::No,
- true,
+ HasGenericParams::Yes,
None,
|this| this.visit_expr(expr),
);
// so it doesn't matter whether this is a trivial constant.
this.with_constant_rib(
IsRepeatExpr::No,
- true,
+ HasGenericParams::Yes,
Some((item.ident, constant_item_kind)),
|this| this.visit_expr(expr),
);
// Note that we intentionally still forbid `[0; N + 1]` during
// name resolution so that we don't extend the future
// compat lint to new cases.
+ #[instrument(level = "debug", skip(self, f))]
fn with_constant_rib(
&mut self,
is_repeat: IsRepeatExpr,
- is_trivial: bool,
+ may_use_generics: HasGenericParams,
item: Option<(Ident, ConstantItemKind)>,
f: impl FnOnce(&mut Self),
) {
- debug!("with_constant_rib: is_repeat={:?} is_trivial={}", is_repeat, is_trivial);
- self.with_rib(ValueNS, ConstantItemRibKind(is_trivial, item), |this| {
+ self.with_rib(ValueNS, ConstantItemRibKind(may_use_generics, item), |this| {
this.with_rib(
TypeNS,
- ConstantItemRibKind(is_repeat == IsRepeatExpr::Yes || is_trivial, item),
+ ConstantItemRibKind(
+ may_use_generics.force_yes_if(is_repeat == IsRepeatExpr::Yes),
+ item,
+ ),
|this| {
- this.with_label_rib(ConstantItemRibKind(is_trivial, item), f);
+ this.with_label_rib(ConstantItemRibKind(may_use_generics, item), f);
},
)
});
None,
&path,
PathSource::Trait(AliasPossibility::No),
- Finalize::SimplePath(trait_ref.ref_id, trait_ref.path.span),
+ Finalize::new(trait_ref.ref_id, trait_ref.path.span),
);
if let Some(def_id) = res.base_res().opt_def_id() {
new_id = Some(def_id);
// not used as part of the type system, this is far less surprising.
this.with_constant_rib(
IsRepeatExpr::No,
- true,
+ HasGenericParams::Yes,
None,
|this| {
visit::walk_assoc_item(
qself,
&Segment::from_path(path),
source,
- Finalize::SimplePath(id, path.span),
+ Finalize::new(id, path.span),
);
}
);
let ns = source.namespace();
- let (id, path_span) =
- finalize.node_id_and_path_span().expect("unexpected speculative resolution");
+ let Finalize { node_id, path_span, .. } = finalize;
let report_errors = |this: &mut Self, res: Option<Res>| {
if this.should_report_errs() {
let (err, candidates) =
def_id,
instead,
suggestion,
+ path: path.into(),
});
}
def_id,
instead: false,
suggestion: None,
+ path: path.into(),
});
} else {
err.cancel();
if ns == ValueNS {
let item_name = path.last().unwrap().ident;
let traits = self.traits_in_scope(item_name, ns);
- self.r.trait_map.insert(id, traits);
+ self.r.trait_map.insert(node_id, traits);
}
if PrimTy::from_name(path[0].ident.name).is_some() {
std_path.push(Segment::from_ident(Ident::with_dummy_span(sym::std)));
std_path.extend(path);
if let PathResult::Module(_) | PathResult::NonModule(_) =
- self.resolve_path(&std_path, Some(ns), Finalize::No)
+ self.resolve_path(&std_path, Some(ns), None)
{
// Check if we wrote `str::from_utf8` instead of `std::str::from_utf8`
let item_span =
if !matches!(source, PathSource::TraitItem(..)) {
// Avoid recording definition of `A::B` in `<T as A>::B::C`.
- self.r.record_partial_res(id, partial_res);
- self.resolve_elided_lifetimes_in_path(id, partial_res, path, source, finalize);
+ self.r.record_partial_res(node_id, partial_res);
+ self.resolve_elided_lifetimes_in_path(node_id, partial_res, path, source, path_span);
}
partial_res
// the trait (the slice upto and including
// `qself.position`). And then we recursively resolve that,
// but with `qself` set to `None`.
- //
- // However, setting `qself` to none (but not changing the
- // span) loses the information about where this path
- // *actually* appears, so for the purposes of the crate
- // lint we pass along information that this is the trait
- // name from a fully qualified path, and this also
- // contains the full span (the `Finalize::QPathTrait`).
let ns = if qself.position + 1 == path.len() { ns } else { TypeNS };
let partial_res = self.smart_resolve_path_fragment(
None,
&path[..=qself.position],
PathSource::TraitItem(ns),
- finalize.node_id_and_path_span().map_or(Finalize::No, |(qpath_id, path_span)| {
- Finalize::QPathTrait { qpath_id, qpath_span: qself.path_span, path_span }
- }),
+ Finalize::with_root_span(finalize.node_id, finalize.path_span, qself.path_span),
);
// The remaining segments (the `C` in our example) will
)));
}
- let result = match self.resolve_path(&path, Some(ns), finalize) {
+ let result = match self.resolve_path(&path, Some(ns), Some(finalize)) {
PathResult::NonModule(path_res) => path_res,
PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => {
PartialRes::new(module.res().unwrap())
&& result.base_res() != Res::Err
&& path[0].ident.name != kw::PathRoot
&& path[0].ident.name != kw::DollarCrate
- && let Some((id, path_span)) = finalize.node_id_and_path_span()
{
let unqualified_result = {
- match self.resolve_path(&[*path.last().unwrap()], Some(ns), Finalize::No) {
+ match self.resolve_path(&[*path.last().unwrap()], Some(ns), None) {
PathResult::NonModule(path_res) => path_res.base_res(),
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
module.res().unwrap()
};
if result.base_res() == unqualified_result {
let lint = lint::builtin::UNUSED_QUALIFICATIONS;
- self.r.lint_buffer.buffer_lint(lint, id, path_span, "unnecessary qualification")
+ self.r.lint_buffer.buffer_lint(
+ lint,
+ finalize.node_id,
+ finalize.path_span,
+ "unnecessary qualification",
+ )
}
}
debug!("resolve_anon_const {:?} is_repeat: {:?}", constant, is_repeat);
self.with_constant_rib(
is_repeat,
- constant.value.is_potential_trivial_const_param(),
+ if constant.value.is_potential_trivial_const_param() {
+ HasGenericParams::Yes
+ } else {
+ HasGenericParams::No
+ },
None,
|this| visit::walk_anon_const(this, constant),
);
if const_args.contains(&idx) {
self.with_constant_rib(
IsRepeatExpr::No,
- argument.is_potential_trivial_const_param(),
+ if argument.is_potential_trivial_const_param() {
+ HasGenericParams::Yes
+ } else {
+ HasGenericParams::No
+ },
None,
|this| {
this.resolve_expr(argument, None);
use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
use crate::late::{LifetimeBinderKind, LifetimeRibKind};
use crate::path_names_to_string;
-use crate::{Finalize, Module, ModuleKind, ModuleOrUniformRoot};
+use crate::{Module, ModuleKind, ModuleOrUniformRoot};
use crate::{PathResult, PathSource, Segment};
use rustc_ast::visit::FnKind;
(String::new(), "the crate root".to_string())
} else {
let mod_path = &path[..path.len() - 1];
- let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), Finalize::No) {
+ let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) {
PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),
_ => None,
}
if let crate::PathSource::TraitItem(_) = source {
let mod_path = &path[..path.len() - 1];
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
- self.resolve_path(mod_path, None, Finalize::No)
+ self.resolve_path(mod_path, None, None)
{
let resolutions = self.r.resolutions(module).borrow();
let targets: Vec<_> =
// Search in module.
let mod_path = &path[..path.len() - 1];
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
- self.resolve_path(mod_path, Some(TypeNS), Finalize::No)
+ self.resolve_path(mod_path, Some(TypeNS), None)
{
self.r.add_module_candidates(module, &mut names, &filter_fn);
}
) -> Option<(LocalDefId, &'tcx FxHashSet<LocalDefId>)> {
match tcx.def_kind(def_id) {
DefKind::AnonConst | DefKind::InlineConst => {
- let mut def_id = tcx
- .parent(def_id.to_def_id())
- .unwrap_or_else(|| bug!("anon const or closure without a parent"));
+ let mut def_id = tcx.local_parent(def_id);
// We search for the next outer anon const or fn here
// while skipping closures.
//
// Note that for `AnonConst` we still just recurse until we
// find a function body, but who cares :shrug:
- while tcx.is_closure(def_id) {
- def_id = tcx
- .parent(def_id)
- .unwrap_or_else(|| bug!("anon const or closure without a parent"));
+ while tcx.is_closure(def_id.to_def_id()) {
+ def_id = tcx.local_parent(def_id);
}
- tcx.is_late_bound_map(def_id.expect_local())
+ tcx.is_late_bound_map(def_id)
}
_ => resolve_lifetimes_for(tcx, def_id).late_bound.get(&def_id).map(|lt| (def_id, lt)),
}
let remove_decl = self
.tcx
.parent(def_id)
- .and_then(|parent_def_id| parent_def_id.as_local())
+ .as_local()
.and_then(|parent_def_id| self.tcx.hir().get_generics(parent_def_id))
.and_then(|generics| self.lifetime_deletion_span(name, generics));
continue;
}
- if let Some(parent_def_id) = self.tcx.parent(def_id) {
- if let Some(def_id) = parent_def_id.as_local() {
- // lifetimes in `derive` expansions don't count (Issue #53738)
- if self
- .tcx
- .get_attrs(def_id.to_def_id())
- .iter()
- .any(|attr| attr.has_name(sym::automatically_derived))
- {
- continue;
- }
+ let parent_def_id = self.tcx.parent(def_id);
+ if let Some(def_id) = parent_def_id.as_local() {
+ // lifetimes in `derive` expansions don't count (Issue #53738)
+ if self
+ .tcx
+ .get_attrs(def_id.to_def_id())
+ .iter()
+ .any(|attr| attr.has_name(sym::automatically_derived))
+ {
+ continue;
+ }
- // opaque types generated when desugaring an async function can have a single
- // use lifetime even if it is explicitly denied (Issue #77175)
- if let hir::Node::Item(hir::Item {
- kind: hir::ItemKind::OpaqueTy(ref opaque),
- ..
- }) = self.tcx.hir().get_by_def_id(def_id)
- {
- if !matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(..)) {
+ // opaque types generated when desugaring an async function can have a single
+ // use lifetime even if it is explicitly denied (Issue #77175)
+ if let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::OpaqueTy(ref opaque),
+ ..
+ }) = self.tcx.hir().get_by_def_id(def_id)
+ {
+ if !matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(..)) {
+ continue 'lifetimes;
+ }
+ // We want to do this only if the lifetime identifier is already defined
+ // in the async function that generated this. Otherwise it could be
+ // an opaque type defined by the developer and we still want this
+ // lint to fail compilation
+ for p in opaque.generics.params {
+ if defined_by.contains_key(&p.name) {
continue 'lifetimes;
}
- // We want to do this only if the lifetime identifier is already defined
- // in the async function that generated this. Otherwise it could be
- // an opaque type defined by the developer and we still want this
- // lint to fail compilation
- for p in opaque.generics.params {
- if defined_by.contains_key(&p.name) {
- continue 'lifetimes;
- }
- }
}
}
}
|lint| {
let mut err = lint
.build(&format!("lifetime parameter `{}` never used", name));
- if let Some(parent_def_id) = self.tcx.parent(def_id) {
- if let Some(generics) =
- self.tcx.hir().get_generics(parent_def_id.expect_local())
- {
- let unused_lt_span =
- self.lifetime_deletion_span(name, generics);
- if let Some(span) = unused_lt_span {
- err.span_suggestion(
- span,
- "elide the unused lifetime",
- String::new(),
- Applicability::MachineApplicable,
- );
- }
+ let parent_def_id = self.tcx.parent(def_id);
+ if let Some(generics) =
+ self.tcx.hir().get_generics(parent_def_id.expect_local())
+ {
+ let unused_lt_span =
+ self.lifetime_deletion_span(name, generics);
+ if let Some(span) = unused_lt_span {
+ err.span_suggestion(
+ span,
+ "elide the unused lifetime",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
}
}
err.emit();
/// but not for late resolution yet.
#[derive(Clone, Copy, Debug)]
pub struct ParentScope<'a> {
- module: Module<'a>,
+ pub module: Module<'a>,
expansion: LocalExpnId,
- macro_rules: MacroRulesScopeRef<'a>,
+ pub macro_rules: MacroRulesScopeRef<'a>,
derives: &'a [ast::Path],
}
instead: bool,
/// Extra free-form suggestion.
suggestion: Option<(Span, &'static str, String, Applicability)>,
+ /// Path `Segment`s at the place of use that failed. Used for accurate suggestion after telling
+ /// the user to import the item directly.
+ path: Vec<Segment>,
}
#[derive(Clone, Copy, PartialEq, Debug)]
/// `macro_rules` scopes *produced* by expanding the macro invocations,
/// include all the `macro_rules` items and other invocations generated by them.
output_macro_rules_scopes: FxHashMap<LocalExpnId, MacroRulesScopeRef<'a>>,
+ /// `macro_rules` scopes produced by `macro_rules` item definitions.
+ macro_rules_scopes: FxHashMap<LocalDefId, MacroRulesScopeRef<'a>>,
/// Helper attributes that are in scope for the given expansion.
helper_attrs: FxHashMap<LocalExpnId, Vec<Ident>>,
/// Ready or in-progress results of resolving paths inside the `#[derive(...)]` attribute
}
impl<'a, 'b> DefIdTree for &'a Resolver<'b> {
- fn parent(self, id: DefId) -> Option<DefId> {
+ #[inline]
+ fn opt_parent(self, id: DefId) -> Option<DefId> {
match id.as_local() {
Some(id) => self.definitions.def_key(id).parent,
None => self.cstore().def_key(id).parent,
non_macro_attr: Lrc::new(SyntaxExtension::non_macro_attr(session.edition())),
invocation_parent_scopes: Default::default(),
output_macro_rules_scopes: Default::default(),
+ macro_rules_scopes: Default::default(),
helper_attrs: Default::default(),
derive_data: Default::default(),
local_macro_def_scopes: FxHashMap::default(),
&mut self,
path_str: &str,
ns: Namespace,
- mut module_id: DefId,
+ mut parent_scope: ParentScope<'a>,
) -> Option<Res> {
let mut segments =
Vec::from_iter(path_str.split("::").map(Ident::from_str).map(Segment::from_ident));
if let Some(segment) = segments.first_mut() {
if segment.ident.name == kw::Crate {
// FIXME: `resolve_path` always resolves `crate` to the current crate root, but
- // rustdoc wants it to resolve to the `module_id`'s crate root. This trick of
+ // rustdoc wants it to resolve to the `parent_scope`'s crate root. This trick of
// replacing `crate` with `self` and changing the current module should achieve
// the same effect.
segment.ident.name = kw::SelfLower;
- module_id = module_id.krate.as_def_id();
+ parent_scope.module =
+ self.expect_module(parent_scope.module.def_id().krate.as_def_id());
} else if segment.ident.name == kw::Empty {
segment.ident.name = kw::PathRoot;
}
}
- let module = self.expect_module(module_id);
- match self.maybe_resolve_path(&segments, Some(ns), &ParentScope::module(module, self)) {
+ match self.maybe_resolve_path(&segments, Some(ns), &parent_scope) {
PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()),
PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
Some(path_res.base_res())
}
}
- // For rustdoc.
- pub fn graph_root(&self) -> Module<'a> {
- self.graph_root
- }
-
// For rustdoc.
pub fn take_all_macro_rules(&mut self) -> FxHashMap<Symbol, Res> {
mem::take(&mut self.all_macro_rules)
}
}
+ /// For rustdoc.
+ pub fn macro_rules_scope(&self, def_id: LocalDefId) -> MacroRulesScopeRef<'a> {
+ *self.macro_rules_scopes.get(&def_id).expect("not a `macro_rules` item")
+ }
+
/// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
#[inline]
pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
}
#[derive(Copy, Clone, Debug)]
-enum Finalize {
- /// Do not issue the lint.
- No,
-
- /// This lint applies to some arbitrary path; e.g., `impl ::foo::Bar`.
- /// In this case, we can take the span of that path.
- SimplePath(NodeId, Span),
-
- /// This lint comes from a `use` statement. In this case, what we
- /// care about really is the *root* `use` statement; e.g., if we
- /// have nested things like `use a::{b, c}`, we care about the
- /// `use a` part.
- UsePath { root_id: NodeId, root_span: Span, path_span: Span },
-
- /// This is the "trait item" from a fully qualified path. For example,
- /// we might be resolving `X::Y::Z` from a path like `<T as X::Y>::Z`.
- /// The `path_span` is the span of the to the trait itself (`X::Y`).
- QPathTrait { qpath_id: NodeId, qpath_span: Span, path_span: Span },
+struct Finalize {
+ /// Node ID for linting.
+ node_id: NodeId,
+ /// Span of the whole path or some its characteristic fragment.
+ /// E.g. span of `b` in `foo::{a, b, c}`, or full span for regular paths.
+ path_span: Span,
+ /// Span of the path start, suitable for prepending something to to it.
+ /// E.g. span of `foo` in `foo::{a, b, c}`, or full span for regular paths.
+ root_span: Span,
+ /// Whether to report privacy errors or silently return "no resolution" for them,
+ /// similarly to speculative resolution.
+ report_private: bool,
}
impl Finalize {
- fn node_id_and_path_span(&self) -> Option<(NodeId, Span)> {
- match *self {
- Finalize::No => None,
- Finalize::SimplePath(id, path_span)
- | Finalize::UsePath { root_id: id, path_span, .. }
- | Finalize::QPathTrait { qpath_id: id, path_span, .. } => Some((id, path_span)),
- }
- }
-
- fn node_id(&self) -> Option<NodeId> {
- self.node_id_and_path_span().map(|(id, _)| id)
+ fn new(node_id: NodeId, path_span: Span) -> Finalize {
+ Finalize::with_root_span(node_id, path_span, path_span)
}
- fn path_span(&self) -> Option<Span> {
- self.node_id_and_path_span().map(|(_, path_span)| path_span)
+ fn with_root_span(node_id: NodeId, path_span: Span, root_span: Span) -> Finalize {
+ Finalize { node_id, path_span, root_span, report_private: true }
}
}
parent_scope,
None,
force,
- false,
None,
);
if let Err(Determinacy::Undetermined) = binding {
&path,
Some(MacroNS),
&parent_scope,
- Finalize::SimplePath(ast::CRATE_NODE_ID, path_span),
+ Some(Finalize::new(ast::CRATE_NODE_ID, path_span)),
None,
) {
PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
ident,
ScopeSet::Macro(kind),
&parent_scope,
- Some(ident.span),
+ Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)),
true,
- false,
None,
) {
Ok(binding) => {
ident,
ScopeSet::Macro(MacroKind::Attr),
&parent_scope,
- Some(ident.span),
+ Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)),
true,
- false,
None,
);
}
let access = access_from!(self.save_ctxt, item.def_id);
let ref_id = self.lookup_def_id(item.hir_id()).map(id_from_def_id);
let span = self.span_from_span(sub_span);
- let parent =
- self.save_ctxt.tcx.parent(item.def_id.to_def_id()).map(id_from_def_id);
+ let parent = self.save_ctxt.tcx.local_parent(item.def_id);
self.dumper.import(
&access,
Import {
alias_span: None,
name: item.ident.to_string(),
value: String::new(),
- parent,
+ parent: Some(id_from_def_id(parent.to_def_id())),
},
);
self.write_sub_paths_truncated(&path);
if !self.span.filter_generated(item.span) {
let access = access_from!(self.save_ctxt, item.def_id);
let span = self.span_from_span(sub_span);
- let parent =
- self.save_ctxt.tcx.parent(item.def_id.to_def_id()).map(id_from_def_id);
+ let parent = self.save_ctxt.tcx.local_parent(item.def_id);
self.dumper.import(
&access,
Import {
alias_span: None,
name: "*".to_owned(),
value: names.join(", "),
- parent,
+ parent: Some(id_from_def_id(parent.to_def_id())),
},
);
self.write_sub_paths(&path);
let name_span = item.ident.span;
if !self.span.filter_generated(name_span) {
let span = self.span_from_span(name_span);
- let parent =
- self.save_ctxt.tcx.parent(item.def_id.to_def_id()).map(id_from_def_id);
+ let parent = self.save_ctxt.tcx.local_parent(item.def_id);
self.dumper.import(
&Access { public: false, reachable: false },
Import {
alias_span: None,
name: item.ident.to_string(),
value: String::new(),
- parent,
+ parent: Some(id_from_def_id(parent.to_def_id())),
},
);
}
// This is a reference to a tuple struct or an enum variant where the def_id points
// to an invisible constructor function. That is not a very useful
// def, so adjust to point to the tuple struct or enum variant itself.
- let parent_def_id = self.tcx.parent(def_id).unwrap();
+ let parent_def_id = self.tcx.parent(def_id);
Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(parent_def_id) })
}
Res::Def(HirDefKind::Static(_) | HirDefKind::Const | HirDefKind::AssocConst, _) => {
CondTemporary,
QuestionMark,
TryBlock,
+ YeetExpr,
/// Desugaring of an `impl Trait` in return type position
/// to an `type Foo = impl Trait;` and replacing the
/// `impl Trait` with `Foo`.
DesugaringKind::Await => "`await` expression",
DesugaringKind::QuestionMark => "operator `?`",
DesugaringKind::TryBlock => "`try` block",
+ DesugaringKind::YeetExpr => "`do yeet` expression",
DesugaringKind::OpaqueTy => "`impl Trait`",
DesugaringKind::ForLoop => "`for` loop",
DesugaringKind::LetElse => "`let...else`",
MacroRules: "macro_rules",
Raw: "raw",
Union: "union",
+ Yeet: "yeet",
}
// Pre-interned symbols that can be referred to with `rustc_span::sym::*`.
from_residual,
from_size_align_unchecked,
from_usize,
+ from_yeet,
fsub_fast,
fundamental,
future,
x87_reg,
xer,
xmm_reg,
+ yeet_desugar_details,
+ yeet_expr,
ymm_reg,
zmm_reg,
}
return false;
}
- let orig_ty = old_pred.self_ty().skip_binder();
+ // This is a quick fix to resolve an ICE (#96223).
+ // This change should probably be deeper.
+ // As suggested by @jackh726, `mk_trait_obligation_with_new_self_ty` could take a `Binder<(TraitRef, Ty)>
+ // instead of `Binder<Ty>` leading to some changes to its call places.
+ let Some(orig_ty) = old_pred.self_ty().no_bound_vars() else {
+ return false;
+ };
let mk_result = |new_ty| {
let obligation =
self.mk_trait_obligation_with_new_self_ty(param_env, old_pred, new_ty);
GeneratorKind::Async(AsyncGeneratorKind::Fn) => self
.tcx
.parent(generator_did)
- .and_then(|parent_did| parent_did.as_local())
+ .as_local()
.map(|parent_did| hir.local_def_id_to_hir_id(parent_did))
.and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
.map(|name| {
match token {
Piece::String(_) => (), // Normal string, no need to check it
Piece::NextArgument(a) => match a.position {
- // `{Self}` is allowed
- Position::ArgumentNamed(s, _) if s == kw::SelfUpper => (),
- // `{ThisTraitsName}` is allowed
- Position::ArgumentNamed(s, _) if s == trait_name => (),
- // `{from_method}` is allowed
- Position::ArgumentNamed(s, _) if s == sym::from_method => (),
- // `{from_desugaring}` is allowed
- Position::ArgumentNamed(s, _) if s == sym::from_desugaring => (),
- // `{ItemContext}` is allowed
- Position::ArgumentNamed(s, _) if s == sym::ItemContext => (),
- // `{integral}` and `{integer}` and `{float}` are allowed
- Position::ArgumentNamed(s, _)
- if s == sym::integral || s == sym::integer_ || s == sym::float =>
- {
- ()
- }
- // So is `{A}` if A is a type parameter
Position::ArgumentNamed(s, _) => {
- match generics.params.iter().find(|param| param.name == s) {
- Some(_) => (),
- None => {
- let reported = struct_span_err!(
- tcx.sess,
- span,
- E0230,
- "there is no parameter `{}` on {}",
- s,
- if trait_def_id == item_def_id {
- format!("trait `{}`", trait_name)
- } else {
- "impl".to_string()
- }
- )
- .emit();
- result = Err(reported);
- }
+ match Symbol::intern(s) {
+ // `{Self}` is allowed
+ kw::SelfUpper => (),
+ // `{ThisTraitsName}` is allowed
+ s if s == trait_name => (),
+ // `{from_method}` is allowed
+ sym::from_method => (),
+ // `{from_desugaring}` is allowed
+ sym::from_desugaring => (),
+ // `{ItemContext}` is allowed
+ sym::ItemContext => (),
+ // `{integral}` and `{integer}` and `{float}` are allowed
+ sym::integral | sym::integer_ | sym::float => (),
+ // So is `{A}` if A is a type parameter
+ s => match generics.params.iter().find(|param| param.name == s) {
+ Some(_) => (),
+ None => {
+ let reported = struct_span_err!(
+ tcx.sess,
+ span,
+ E0230,
+ "there is no parameter `{}` on {}",
+ s,
+ if trait_def_id == item_def_id {
+ format!("trait `{}`", trait_name)
+ } else {
+ "impl".to_string()
+ }
+ )
+ .emit();
+ result = Err(reported);
+ }
+ },
}
}
// `{:1}` and `{}` are not to be used
.map(|p| match p {
Piece::String(s) => s,
Piece::NextArgument(a) => match a.position {
- Position::ArgumentNamed(s, _) => match generic_map.get(&s) {
- Some(val) => val,
- None if s == name => &trait_str,
- None => {
- if let Some(val) = options.get(&s) {
- val
- } else if s == sym::from_desugaring || s == sym::from_method {
- // don't break messages using these two arguments incorrectly
- &empty_string
- } else if s == sym::ItemContext {
- &item_context
- } else if s == sym::integral {
- "{integral}"
- } else if s == sym::integer_ {
- "{integer}"
- } else if s == sym::float {
- "{float}"
- } else {
- bug!(
- "broken on_unimplemented {:?} for {:?}: \
+ Position::ArgumentNamed(s, _) => {
+ let s = Symbol::intern(s);
+ match generic_map.get(&s) {
+ Some(val) => val,
+ None if s == name => &trait_str,
+ None => {
+ if let Some(val) = options.get(&s) {
+ val
+ } else if s == sym::from_desugaring || s == sym::from_method {
+ // don't break messages using these two arguments incorrectly
+ &empty_string
+ } else if s == sym::ItemContext {
+ &item_context
+ } else if s == sym::integral {
+ "{integral}"
+ } else if s == sym::integer_ {
+ "{integer}"
+ } else if s == sym::float {
+ "{float}"
+ } else {
+ bug!(
+ "broken on_unimplemented {:?} for {:?}: \
no argument matching {:?}",
- self.0,
- trait_ref,
- s
- )
+ self.0,
+ trait_ref,
+ s
+ )
+ }
}
}
- },
+ }
_ => bug!("broken on_unimplemented {:?} - bad format arg", self.0),
},
})
) -> Ty<'tcx> {
let tcx = self.tcx();
- let trait_def_id = tcx.parent(item_def_id).unwrap();
+ let trait_def_id = tcx.parent(item_def_id);
debug!("qpath_to_ty: trait_def_id={:?}", trait_def_id);
// `DefKind::Ctor` -> `DefKind::Variant`
if let DefKind::Ctor(..) = kind {
- def_id = tcx.parent(def_id).unwrap()
+ def_id = tcx.parent(def_id);
}
// `DefKind::Variant` -> `DefKind::Enum`
- let enum_def_id = tcx.parent(def_id).unwrap();
+ let enum_def_id = tcx.parent(def_id);
(enum_def_id, last - 1)
} else {
// FIXME: lint here recommending `Enum::<...>::Variant` form
let def_kind = self.tcx.def_kind(def_id);
let item_ty = if let DefKind::Variant = def_kind {
- self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent"))
+ self.tcx.type_of(self.tcx.parent(def_id))
} else {
self.tcx.type_of(def_id)
};
errors.drain_filter(|error| {
let Error::Invalid(input_idx, Compatibility::Incompatible(error)) = error else { return false };
let expected_ty = expected_input_tys[*input_idx];
- let provided_ty = final_arg_types[*input_idx].map(|ty| ty.0).unwrap();
+ let Some(Some((provided_ty, _))) = final_arg_types.get(*input_idx) else { return false };
let cause = &self.misc(provided_args[*input_idx].span);
- let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
+ let trace = TypeTrace::types(cause, true, expected_ty, *provided_ty);
if let Some(e) = error {
if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) {
self.report_and_explain_type_error(trace, e).emit();
Error::Invalid(input_idx, compatibility) => {
let expected_ty = expected_input_tys[input_idx];
if let Compatibility::Incompatible(error) = &compatibility {
- let provided_ty = final_arg_types[input_idx].map(|ty| ty.0).unwrap();
- let cause = &self.misc(provided_args[input_idx].span);
+ let provided_ty = final_arg_types
+ .get(input_idx)
+ .and_then(|x| x.as_ref())
+ .map(|ty| ty.0)
+ .unwrap_or(tcx.ty_error());
+ let cause = &self.misc(
+ provided_args.get(input_idx).map(|i| i.span).unwrap_or(call_span),
+ );
let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
if let Some(e) = error {
self.note_type_err(
}
}
- self.emit_coerce_suggestions(
- &mut err,
- &provided_args[input_idx],
- final_arg_types[input_idx].map(|ty| ty.0).unwrap(),
- final_arg_types[input_idx].map(|ty| ty.1).unwrap(),
- None,
- None,
- );
+ if let Some(expr) = provided_args.get(input_idx) {
+ self.emit_coerce_suggestions(
+ &mut err,
+ &expr,
+ final_arg_types[input_idx].map(|ty| ty.0).unwrap(),
+ final_arg_types[input_idx].map(|ty| ty.1).unwrap(),
+ None,
+ None,
+ );
+ }
}
Error::Extra(arg_idx) => {
let arg_type = if let Some((_, ty)) = final_arg_types[arg_idx] {
);
for (idx, arg) in matched_inputs.iter().enumerate() {
let suggestion_text = if let Some(arg) = arg {
- let arg_span = provided_args[*arg].span;
+ let arg_span = provided_args[*arg].span.source_callsite();
let arg_text = source_map.span_to_snippet(arg_span).unwrap();
arg_text
} else {
let (candidates, globs): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|trait_did| {
if let Some(parent_did) = parent_map.get(trait_did) {
// If the item is re-exported as `_`, we should suggest a glob-import instead.
- if Some(*parent_did) != self.tcx.parent(*trait_did)
+ if *parent_did != self.tcx.parent(*trait_did)
&& self
.tcx
.module_children(*parent_did)
// #73631: closures inherit `#[target_feature]` annotations
if tcx.features().target_feature_11 && tcx.is_closure(id) {
- let owner_id = tcx.parent(id).expect("closure should have a parent");
+ let owner_id = tcx.parent(id);
codegen_fn_attrs
.target_features
.extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied())
Some(hidden) => hidden.ty,
None => {
let span = tcx.def_span(def_id);
- let name = tcx.item_name(tcx.parent(def_id.to_def_id()).unwrap());
+ let name = tcx.item_name(tcx.local_parent(def_id).to_def_id());
let label = format!(
"`{}` must be used in combination with a concrete type within the same module",
name
use super::SpecExtend;
+#[cfg(test)]
+mod tests;
+
/// A priority queue implemented with a binary heap.
///
/// This will be a max-heap.
--- /dev/null
+use super::*;
+use crate::boxed::Box;
+use std::iter::TrustedLen;
+use std::panic::{catch_unwind, AssertUnwindSafe};
+use std::sync::atomic::{AtomicU32, Ordering};
+
+#[test]
+fn test_iterator() {
+ let data = vec![5, 9, 3];
+ let iterout = [9, 5, 3];
+ let heap = BinaryHeap::from(data);
+ let mut i = 0;
+ for el in &heap {
+ assert_eq!(*el, iterout[i]);
+ i += 1;
+ }
+}
+
+#[test]
+fn test_iter_rev_cloned_collect() {
+ let data = vec![5, 9, 3];
+ let iterout = vec![3, 5, 9];
+ let pq = BinaryHeap::from(data);
+
+ let v: Vec<_> = pq.iter().rev().cloned().collect();
+ assert_eq!(v, iterout);
+}
+
+#[test]
+fn test_into_iter_collect() {
+ let data = vec![5, 9, 3];
+ let iterout = vec![9, 5, 3];
+ let pq = BinaryHeap::from(data);
+
+ let v: Vec<_> = pq.into_iter().collect();
+ assert_eq!(v, iterout);
+}
+
+#[test]
+fn test_into_iter_size_hint() {
+ let data = vec![5, 9];
+ let pq = BinaryHeap::from(data);
+
+ let mut it = pq.into_iter();
+
+ assert_eq!(it.size_hint(), (2, Some(2)));
+ assert_eq!(it.next(), Some(9));
+
+ assert_eq!(it.size_hint(), (1, Some(1)));
+ assert_eq!(it.next(), Some(5));
+
+ assert_eq!(it.size_hint(), (0, Some(0)));
+ assert_eq!(it.next(), None);
+}
+
+#[test]
+fn test_into_iter_rev_collect() {
+ let data = vec![5, 9, 3];
+ let iterout = vec![3, 5, 9];
+ let pq = BinaryHeap::from(data);
+
+ let v: Vec<_> = pq.into_iter().rev().collect();
+ assert_eq!(v, iterout);
+}
+
+#[test]
+fn test_into_iter_sorted_collect() {
+ let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
+ let it = heap.into_iter_sorted();
+ let sorted = it.collect::<Vec<_>>();
+ assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]);
+}
+
+#[test]
+fn test_drain_sorted_collect() {
+ let mut heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
+ let it = heap.drain_sorted();
+ let sorted = it.collect::<Vec<_>>();
+ assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]);
+}
+
+fn check_exact_size_iterator<I: ExactSizeIterator>(len: usize, it: I) {
+ let mut it = it;
+
+ for i in 0..it.len() {
+ let (lower, upper) = it.size_hint();
+ assert_eq!(Some(lower), upper);
+ assert_eq!(lower, len - i);
+ assert_eq!(it.len(), len - i);
+ it.next();
+ }
+ assert_eq!(it.len(), 0);
+ assert!(it.is_empty());
+}
+
+#[test]
+fn test_exact_size_iterator() {
+ let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
+ check_exact_size_iterator(heap.len(), heap.iter());
+ check_exact_size_iterator(heap.len(), heap.clone().into_iter());
+ check_exact_size_iterator(heap.len(), heap.clone().into_iter_sorted());
+ check_exact_size_iterator(heap.len(), heap.clone().drain());
+ check_exact_size_iterator(heap.len(), heap.clone().drain_sorted());
+}
+
+fn check_trusted_len<I: TrustedLen>(len: usize, it: I) {
+ let mut it = it;
+ for i in 0..len {
+ let (lower, upper) = it.size_hint();
+ if upper.is_some() {
+ assert_eq!(Some(lower), upper);
+ assert_eq!(lower, len - i);
+ }
+ it.next();
+ }
+}
+
+#[test]
+fn test_trusted_len() {
+ let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
+ check_trusted_len(heap.len(), heap.clone().into_iter_sorted());
+ check_trusted_len(heap.len(), heap.clone().drain_sorted());
+}
+
+#[test]
+fn test_peek_and_pop() {
+ let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
+ let mut sorted = data.clone();
+ sorted.sort();
+ let mut heap = BinaryHeap::from(data);
+ while !heap.is_empty() {
+ assert_eq!(heap.peek().unwrap(), sorted.last().unwrap());
+ assert_eq!(heap.pop().unwrap(), sorted.pop().unwrap());
+ }
+}
+
+#[test]
+fn test_peek_mut() {
+ let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
+ let mut heap = BinaryHeap::from(data);
+ assert_eq!(heap.peek(), Some(&10));
+ {
+ let mut top = heap.peek_mut().unwrap();
+ *top -= 2;
+ }
+ assert_eq!(heap.peek(), Some(&9));
+}
+
+#[test]
+fn test_peek_mut_pop() {
+ let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
+ let mut heap = BinaryHeap::from(data);
+ assert_eq!(heap.peek(), Some(&10));
+ {
+ let mut top = heap.peek_mut().unwrap();
+ *top -= 2;
+ assert_eq!(PeekMut::pop(top), 8);
+ }
+ assert_eq!(heap.peek(), Some(&9));
+}
+
+#[test]
+fn test_push() {
+ let mut heap = BinaryHeap::from(vec![2, 4, 9]);
+ assert_eq!(heap.len(), 3);
+ assert!(*heap.peek().unwrap() == 9);
+ heap.push(11);
+ assert_eq!(heap.len(), 4);
+ assert!(*heap.peek().unwrap() == 11);
+ heap.push(5);
+ assert_eq!(heap.len(), 5);
+ assert!(*heap.peek().unwrap() == 11);
+ heap.push(27);
+ assert_eq!(heap.len(), 6);
+ assert!(*heap.peek().unwrap() == 27);
+ heap.push(3);
+ assert_eq!(heap.len(), 7);
+ assert!(*heap.peek().unwrap() == 27);
+ heap.push(103);
+ assert_eq!(heap.len(), 8);
+ assert!(*heap.peek().unwrap() == 103);
+}
+
+#[test]
+fn test_push_unique() {
+ let mut heap = BinaryHeap::<Box<_>>::from(vec![box 2, box 4, box 9]);
+ assert_eq!(heap.len(), 3);
+ assert!(**heap.peek().unwrap() == 9);
+ heap.push(box 11);
+ assert_eq!(heap.len(), 4);
+ assert!(**heap.peek().unwrap() == 11);
+ heap.push(box 5);
+ assert_eq!(heap.len(), 5);
+ assert!(**heap.peek().unwrap() == 11);
+ heap.push(box 27);
+ assert_eq!(heap.len(), 6);
+ assert!(**heap.peek().unwrap() == 27);
+ heap.push(box 3);
+ assert_eq!(heap.len(), 7);
+ assert!(**heap.peek().unwrap() == 27);
+ heap.push(box 103);
+ assert_eq!(heap.len(), 8);
+ assert!(**heap.peek().unwrap() == 103);
+}
+
+fn check_to_vec(mut data: Vec<i32>) {
+ let heap = BinaryHeap::from(data.clone());
+ let mut v = heap.clone().into_vec();
+ v.sort();
+ data.sort();
+
+ assert_eq!(v, data);
+ assert_eq!(heap.into_sorted_vec(), data);
+}
+
+#[test]
+fn test_to_vec() {
+ check_to_vec(vec![]);
+ check_to_vec(vec![5]);
+ check_to_vec(vec![3, 2]);
+ check_to_vec(vec![2, 3]);
+ check_to_vec(vec![5, 1, 2]);
+ check_to_vec(vec![1, 100, 2, 3]);
+ check_to_vec(vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 0]);
+ check_to_vec(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
+ check_to_vec(vec![9, 11, 9, 9, 9, 9, 11, 2, 3, 4, 11, 9, 0, 0, 0, 0]);
+ check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+ check_to_vec(vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]);
+ check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 1, 2]);
+ check_to_vec(vec![5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1]);
+}
+
+#[test]
+fn test_in_place_iterator_specialization() {
+ let src: Vec<usize> = vec![1, 2, 3];
+ let src_ptr = src.as_ptr();
+ let heap: BinaryHeap<_> = src.into_iter().map(std::convert::identity).collect();
+ let heap_ptr = heap.iter().next().unwrap() as *const usize;
+ assert_eq!(src_ptr, heap_ptr);
+ let sink: Vec<_> = heap.into_iter().map(std::convert::identity).collect();
+ let sink_ptr = sink.as_ptr();
+ assert_eq!(heap_ptr, sink_ptr);
+}
+
+#[test]
+fn test_empty_pop() {
+ let mut heap = BinaryHeap::<i32>::new();
+ assert!(heap.pop().is_none());
+}
+
+#[test]
+fn test_empty_peek() {
+ let empty = BinaryHeap::<i32>::new();
+ assert!(empty.peek().is_none());
+}
+
+#[test]
+fn test_empty_peek_mut() {
+ let mut empty = BinaryHeap::<i32>::new();
+ assert!(empty.peek_mut().is_none());
+}
+
+#[test]
+fn test_from_iter() {
+ let xs = vec![9, 8, 7, 6, 5, 4, 3, 2, 1];
+
+ let mut q: BinaryHeap<_> = xs.iter().rev().cloned().collect();
+
+ for &x in &xs {
+ assert_eq!(q.pop().unwrap(), x);
+ }
+}
+
+#[test]
+fn test_drain() {
+ let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect();
+
+ assert_eq!(q.drain().take(5).count(), 5);
+
+ assert!(q.is_empty());
+}
+
+#[test]
+fn test_drain_sorted() {
+ let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect();
+
+ assert_eq!(q.drain_sorted().take(5).collect::<Vec<_>>(), vec![9, 8, 7, 6, 5]);
+
+ assert!(q.is_empty());
+}
+
+#[test]
+fn test_drain_sorted_leak() {
+ static DROPS: AtomicU32 = AtomicU32::new(0);
+
+ #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
+ struct D(u32, bool);
+
+ impl Drop for D {
+ fn drop(&mut self) {
+ DROPS.fetch_add(1, Ordering::SeqCst);
+
+ if self.1 {
+ panic!("panic in `drop`");
+ }
+ }
+ }
+
+ let mut q = BinaryHeap::from(vec![
+ D(0, false),
+ D(1, false),
+ D(2, false),
+ D(3, true),
+ D(4, false),
+ D(5, false),
+ ]);
+
+ catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).ok();
+
+ assert_eq!(DROPS.load(Ordering::SeqCst), 6);
+}
+
+#[test]
+fn test_extend_ref() {
+ let mut a = BinaryHeap::new();
+ a.push(1);
+ a.push(2);
+
+ a.extend(&[3, 4, 5]);
+
+ assert_eq!(a.len(), 5);
+ assert_eq!(a.into_sorted_vec(), [1, 2, 3, 4, 5]);
+
+ let mut a = BinaryHeap::new();
+ a.push(1);
+ a.push(2);
+ let mut b = BinaryHeap::new();
+ b.push(3);
+ b.push(4);
+ b.push(5);
+
+ a.extend(&b);
+
+ assert_eq!(a.len(), 5);
+ assert_eq!(a.into_sorted_vec(), [1, 2, 3, 4, 5]);
+}
+
+#[test]
+fn test_append() {
+ let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]);
+ let mut b = BinaryHeap::from(vec![-20, 5, 43]);
+
+ a.append(&mut b);
+
+ assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]);
+ assert!(b.is_empty());
+}
+
+#[test]
+fn test_append_to_empty() {
+ let mut a = BinaryHeap::new();
+ let mut b = BinaryHeap::from(vec![-20, 5, 43]);
+
+ a.append(&mut b);
+
+ assert_eq!(a.into_sorted_vec(), [-20, 5, 43]);
+ assert!(b.is_empty());
+}
+
+#[test]
+fn test_extend_specialization() {
+ let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]);
+ let b = BinaryHeap::from(vec![-20, 5, 43]);
+
+ a.extend(b);
+
+ assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]);
+}
+
+#[allow(dead_code)]
+fn assert_covariance() {
+ fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> {
+ d
+ }
+}
+
+#[test]
+fn test_retain() {
+ let mut a = BinaryHeap::from(vec![100, 10, 50, 1, 2, 20, 30]);
+ a.retain(|&x| x != 2);
+
+ // Check that 20 moved into 10's place.
+ assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]);
+
+ a.retain(|_| true);
+
+ assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]);
+
+ a.retain(|&x| x < 50);
+
+ assert_eq!(a.clone().into_vec(), [30, 20, 10, 1]);
+
+ a.retain(|_| false);
+
+ assert!(a.is_empty());
+}
+
+// old binaryheap failed this test
+//
+// Integrity means that all elements are present after a comparison panics,
+// even if the order might not be correct.
+//
+// Destructors must be called exactly once per element.
+// FIXME: re-enable emscripten once it can unwind again
+#[test]
+#[cfg(not(target_os = "emscripten"))]
+fn panic_safe() {
+ use rand::{seq::SliceRandom, thread_rng};
+ use std::cmp;
+ use std::panic::{self, AssertUnwindSafe};
+ use std::sync::atomic::{AtomicUsize, Ordering};
+
+ static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
+
+ #[derive(Eq, PartialEq, Ord, Clone, Debug)]
+ struct PanicOrd<T>(T, bool);
+
+ impl<T> Drop for PanicOrd<T> {
+ fn drop(&mut self) {
+ // update global drop count
+ DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
+ }
+ }
+
+ impl<T: PartialOrd> PartialOrd for PanicOrd<T> {
+ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
+ if self.1 || other.1 {
+ panic!("Panicking comparison");
+ }
+ self.0.partial_cmp(&other.0)
+ }
+ }
+ let mut rng = thread_rng();
+ const DATASZ: usize = 32;
+ // Miri is too slow
+ let ntest = if cfg!(miri) { 1 } else { 10 };
+
+ // don't use 0 in the data -- we want to catch the zeroed-out case.
+ let data = (1..=DATASZ).collect::<Vec<_>>();
+
+ // since it's a fuzzy test, run several tries.
+ for _ in 0..ntest {
+ for i in 1..=DATASZ {
+ DROP_COUNTER.store(0, Ordering::SeqCst);
+
+ let mut panic_ords: Vec<_> =
+ data.iter().filter(|&&x| x != i).map(|&x| PanicOrd(x, false)).collect();
+ let panic_item = PanicOrd(i, true);
+
+ // heapify the sane items
+ panic_ords.shuffle(&mut rng);
+ let mut heap = BinaryHeap::from(panic_ords);
+ let inner_data;
+
+ {
+ // push the panicking item to the heap and catch the panic
+ let thread_result = {
+ let mut heap_ref = AssertUnwindSafe(&mut heap);
+ panic::catch_unwind(move || {
+ heap_ref.push(panic_item);
+ })
+ };
+ assert!(thread_result.is_err());
+
+ // Assert no elements were dropped
+ let drops = DROP_COUNTER.load(Ordering::SeqCst);
+ assert!(drops == 0, "Must not drop items. drops={}", drops);
+ inner_data = heap.clone().into_vec();
+ drop(heap);
+ }
+ let drops = DROP_COUNTER.load(Ordering::SeqCst);
+ assert_eq!(drops, DATASZ);
+
+ let mut data_sorted = inner_data.into_iter().map(|p| p.0).collect::<Vec<_>>();
+ data_sorted.sort();
+ assert_eq!(data_sorted, data);
+ }
+ }
+}
use super::*;
+use crate::vec::Vec;
+use std::panic::{catch_unwind, AssertUnwindSafe};
use std::thread;
-use std::vec::Vec;
use rand::{thread_rng, RngCore};
+#[test]
+fn test_basic() {
+ let mut m = LinkedList::<Box<_>>::new();
+ assert_eq!(m.pop_front(), None);
+ assert_eq!(m.pop_back(), None);
+ assert_eq!(m.pop_front(), None);
+ m.push_front(box 1);
+ assert_eq!(m.pop_front(), Some(box 1));
+ m.push_back(box 2);
+ m.push_back(box 3);
+ assert_eq!(m.len(), 2);
+ assert_eq!(m.pop_front(), Some(box 2));
+ assert_eq!(m.pop_front(), Some(box 3));
+ assert_eq!(m.len(), 0);
+ assert_eq!(m.pop_front(), None);
+ m.push_back(box 1);
+ m.push_back(box 3);
+ m.push_back(box 5);
+ m.push_back(box 7);
+ assert_eq!(m.pop_front(), Some(box 1));
+
+ let mut n = LinkedList::new();
+ n.push_front(2);
+ n.push_front(3);
+ {
+ assert_eq!(n.front().unwrap(), &3);
+ let x = n.front_mut().unwrap();
+ assert_eq!(*x, 3);
+ *x = 0;
+ }
+ {
+ assert_eq!(n.back().unwrap(), &2);
+ let y = n.back_mut().unwrap();
+ assert_eq!(*y, 2);
+ *y = 1;
+ }
+ assert_eq!(n.pop_front(), Some(0));
+ assert_eq!(n.pop_front(), Some(1));
+}
+
+fn generate_test() -> LinkedList<i32> {
+ list_from(&[0, 1, 2, 3, 4, 5, 6])
+}
+
fn list_from<T: Clone>(v: &[T]) -> LinkedList<T> {
v.iter().cloned().collect()
}
check_links(&n);
}
+#[test]
+fn test_iterator() {
+ let m = generate_test();
+ for (i, elt) in m.iter().enumerate() {
+ assert_eq!(i as i32, *elt);
+ }
+ let mut n = LinkedList::new();
+ assert_eq!(n.iter().next(), None);
+ n.push_front(4);
+ let mut it = n.iter();
+ assert_eq!(it.size_hint(), (1, Some(1)));
+ assert_eq!(it.next().unwrap(), &4);
+ assert_eq!(it.size_hint(), (0, Some(0)));
+ assert_eq!(it.next(), None);
+}
+
+#[test]
+fn test_iterator_clone() {
+ let mut n = LinkedList::new();
+ n.push_back(2);
+ n.push_back(3);
+ n.push_back(4);
+ let mut it = n.iter();
+ it.next();
+ let mut jt = it.clone();
+ assert_eq!(it.next(), jt.next());
+ assert_eq!(it.next_back(), jt.next_back());
+ assert_eq!(it.next(), jt.next());
+}
+
+#[test]
+fn test_iterator_double_end() {
+ let mut n = LinkedList::new();
+ assert_eq!(n.iter().next(), None);
+ n.push_front(4);
+ n.push_front(5);
+ n.push_front(6);
+ let mut it = n.iter();
+ assert_eq!(it.size_hint(), (3, Some(3)));
+ assert_eq!(it.next().unwrap(), &6);
+ assert_eq!(it.size_hint(), (2, Some(2)));
+ assert_eq!(it.next_back().unwrap(), &4);
+ assert_eq!(it.size_hint(), (1, Some(1)));
+ assert_eq!(it.next_back().unwrap(), &5);
+ assert_eq!(it.next_back(), None);
+ assert_eq!(it.next(), None);
+}
+
+#[test]
+fn test_rev_iter() {
+ let m = generate_test();
+ for (i, elt) in m.iter().rev().enumerate() {
+ assert_eq!((6 - i) as i32, *elt);
+ }
+ let mut n = LinkedList::new();
+ assert_eq!(n.iter().rev().next(), None);
+ n.push_front(4);
+ let mut it = n.iter().rev();
+ assert_eq!(it.size_hint(), (1, Some(1)));
+ assert_eq!(it.next().unwrap(), &4);
+ assert_eq!(it.size_hint(), (0, Some(0)));
+ assert_eq!(it.next(), None);
+}
+
+#[test]
+fn test_mut_iter() {
+ let mut m = generate_test();
+ let mut len = m.len();
+ for (i, elt) in m.iter_mut().enumerate() {
+ assert_eq!(i as i32, *elt);
+ len -= 1;
+ }
+ assert_eq!(len, 0);
+ let mut n = LinkedList::new();
+ assert!(n.iter_mut().next().is_none());
+ n.push_front(4);
+ n.push_back(5);
+ let mut it = n.iter_mut();
+ assert_eq!(it.size_hint(), (2, Some(2)));
+ assert!(it.next().is_some());
+ assert!(it.next().is_some());
+ assert_eq!(it.size_hint(), (0, Some(0)));
+ assert!(it.next().is_none());
+}
+
+#[test]
+fn test_iterator_mut_double_end() {
+ let mut n = LinkedList::new();
+ assert!(n.iter_mut().next_back().is_none());
+ n.push_front(4);
+ n.push_front(5);
+ n.push_front(6);
+ let mut it = n.iter_mut();
+ assert_eq!(it.size_hint(), (3, Some(3)));
+ assert_eq!(*it.next().unwrap(), 6);
+ assert_eq!(it.size_hint(), (2, Some(2)));
+ assert_eq!(*it.next_back().unwrap(), 4);
+ assert_eq!(it.size_hint(), (1, Some(1)));
+ assert_eq!(*it.next_back().unwrap(), 5);
+ assert!(it.next_back().is_none());
+ assert!(it.next().is_none());
+}
+
+#[test]
+fn test_mut_rev_iter() {
+ let mut m = generate_test();
+ for (i, elt) in m.iter_mut().rev().enumerate() {
+ assert_eq!((6 - i) as i32, *elt);
+ }
+ let mut n = LinkedList::new();
+ assert!(n.iter_mut().rev().next().is_none());
+ n.push_front(4);
+ let mut it = n.iter_mut().rev();
+ assert!(it.next().is_some());
+ assert!(it.next().is_none());
+}
+
#[test]
fn test_clone_from() {
// Short cloned from long
}
#[test]
-fn test_fuzz() {
- for _ in 0..25 {
- fuzz_test(3);
- fuzz_test(16);
- #[cfg(not(miri))] // Miri is too slow
- fuzz_test(189);
- }
+fn test_eq() {
+ let mut n = list_from(&[]);
+ let mut m = list_from(&[]);
+ assert!(n == m);
+ n.push_front(1);
+ assert!(n != m);
+ m.push_back(1);
+ assert!(n == m);
+
+ let n = list_from(&[2, 3, 4]);
+ let m = list_from(&[1, 2, 3]);
+ assert!(n != m);
+}
+
+#[test]
+fn test_ord() {
+ let n = list_from(&[]);
+ let m = list_from(&[1, 2, 3]);
+ assert!(n < m);
+ assert!(m > n);
+ assert!(n <= n);
+ assert!(n >= n);
+}
+
+#[test]
+fn test_ord_nan() {
+ let nan = 0.0f64 / 0.0;
+ let n = list_from(&[nan]);
+ let m = list_from(&[nan]);
+ assert!(!(n < m));
+ assert!(!(n > m));
+ assert!(!(n <= m));
+ assert!(!(n >= m));
+
+ let n = list_from(&[nan]);
+ let one = list_from(&[1.0f64]);
+ assert!(!(n < one));
+ assert!(!(n > one));
+ assert!(!(n <= one));
+ assert!(!(n >= one));
+
+ let u = list_from(&[1.0f64, 2.0, nan]);
+ let v = list_from(&[1.0f64, 2.0, 3.0]);
+ assert!(!(u < v));
+ assert!(!(u > v));
+ assert!(!(u <= v));
+ assert!(!(u >= v));
+
+ let s = list_from(&[1.0f64, 2.0, 4.0, 2.0]);
+ let t = list_from(&[1.0f64, 2.0, 3.0, 2.0]);
+ assert!(!(s < t));
+ assert!(s > one);
+ assert!(!(s <= one));
+ assert!(s >= one);
}
#[test]
}
}
+#[test]
+fn test_split_off_2() {
+ // singleton
+ {
+ let mut m = LinkedList::new();
+ m.push_back(1);
+
+ let p = m.split_off(0);
+ assert_eq!(m.len(), 0);
+ assert_eq!(p.len(), 1);
+ assert_eq!(p.back(), Some(&1));
+ assert_eq!(p.front(), Some(&1));
+ }
+
+ // not singleton, forwards
+ {
+ let u = vec![1, 2, 3, 4, 5];
+ let mut m = list_from(&u);
+ let mut n = m.split_off(2);
+ assert_eq!(m.len(), 2);
+ assert_eq!(n.len(), 3);
+ for elt in 1..3 {
+ assert_eq!(m.pop_front(), Some(elt));
+ }
+ for elt in 3..6 {
+ assert_eq!(n.pop_front(), Some(elt));
+ }
+ }
+ // not singleton, backwards
+ {
+ let u = vec![1, 2, 3, 4, 5];
+ let mut m = list_from(&u);
+ let mut n = m.split_off(4);
+ assert_eq!(m.len(), 4);
+ assert_eq!(n.len(), 1);
+ for elt in 1..5 {
+ assert_eq!(m.pop_front(), Some(elt));
+ }
+ for elt in 5..6 {
+ assert_eq!(n.pop_front(), Some(elt));
+ }
+ }
+
+ // no-op on the last index
+ {
+ let mut m = LinkedList::new();
+ m.push_back(1);
+
+ let p = m.split_off(1);
+ assert_eq!(m.len(), 1);
+ assert_eq!(p.len(), 0);
+ assert_eq!(m.back(), Some(&1));
+ assert_eq!(m.front(), Some(&1));
+ }
+}
+
fn fuzz_test(sz: i32) {
let mut m: LinkedList<_> = LinkedList::new();
let mut v = vec![];
assert_eq!(i, v.len());
}
+#[test]
+fn test_fuzz() {
+ for _ in 0..25 {
+ fuzz_test(3);
+ fuzz_test(16);
+ #[cfg(not(miri))] // Miri is too slow
+ fuzz_test(189);
+ }
+}
+
+#[test]
+fn test_show() {
+ let list: LinkedList<_> = (0..10).collect();
+ assert_eq!(format!("{list:?}"), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
+
+ let list: LinkedList<_> = ["just", "one", "test", "more"].into_iter().collect();
+ assert_eq!(format!("{list:?}"), "[\"just\", \"one\", \"test\", \"more\"]");
+}
+
#[test]
fn drain_filter_test() {
let mut m: LinkedList<u32> = LinkedList::new();
assert_eq!(c.current(), None);
assert_eq!(c.index, 2);
}
+
+#[test]
+fn test_extend_ref() {
+ let mut a = LinkedList::new();
+ a.push_back(1);
+
+ a.extend(&[2, 3, 4]);
+
+ assert_eq!(a.len(), 4);
+ assert_eq!(a, list_from(&[1, 2, 3, 4]));
+
+ let mut b = LinkedList::new();
+ b.push_back(5);
+ b.push_back(6);
+ a.extend(&b);
+
+ assert_eq!(a.len(), 6);
+ assert_eq!(a, list_from(&[1, 2, 3, 4, 5, 6]));
+}
+
+#[test]
+fn test_extend() {
+ let mut a = LinkedList::new();
+ a.push_back(1);
+ a.extend(vec![2, 3, 4]); // uses iterator
+
+ assert_eq!(a.len(), 4);
+ assert!(a.iter().eq(&[1, 2, 3, 4]));
+
+ let b: LinkedList<_> = [5, 6, 7].into_iter().collect();
+ a.extend(b); // specializes to `append`
+
+ assert_eq!(a.len(), 7);
+ assert!(a.iter().eq(&[1, 2, 3, 4, 5, 6, 7]));
+}
+
+#[test]
+fn test_contains() {
+ let mut l = LinkedList::new();
+ l.extend(&[2, 3, 4]);
+
+ assert!(l.contains(&3));
+ assert!(!l.contains(&1));
+
+ l.clear();
+
+ assert!(!l.contains(&3));
+}
+
+#[test]
+fn drain_filter_empty() {
+ let mut list: LinkedList<i32> = LinkedList::new();
+
+ {
+ let mut iter = list.drain_filter(|_| true);
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ assert_eq!(iter.next(), None);
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ assert_eq!(iter.next(), None);
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ }
+
+ assert_eq!(list.len(), 0);
+ assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
+}
+
+#[test]
+fn drain_filter_zst() {
+ let mut list: LinkedList<_> = [(), (), (), (), ()].into_iter().collect();
+ let initial_len = list.len();
+ let mut count = 0;
+
+ {
+ let mut iter = list.drain_filter(|_| true);
+ assert_eq!(iter.size_hint(), (0, Some(initial_len)));
+ while let Some(_) = iter.next() {
+ count += 1;
+ assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
+ }
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ assert_eq!(iter.next(), None);
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ }
+
+ assert_eq!(count, initial_len);
+ assert_eq!(list.len(), 0);
+ assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
+}
+
+#[test]
+fn drain_filter_false() {
+ let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
+
+ let initial_len = list.len();
+ let mut count = 0;
+
+ {
+ let mut iter = list.drain_filter(|_| false);
+ assert_eq!(iter.size_hint(), (0, Some(initial_len)));
+ for _ in iter.by_ref() {
+ count += 1;
+ }
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ assert_eq!(iter.next(), None);
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ }
+
+ assert_eq!(count, 0);
+ assert_eq!(list.len(), initial_len);
+ assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+}
+
+#[test]
+fn drain_filter_true() {
+ let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
+
+ let initial_len = list.len();
+ let mut count = 0;
+
+ {
+ let mut iter = list.drain_filter(|_| true);
+ assert_eq!(iter.size_hint(), (0, Some(initial_len)));
+ while let Some(_) = iter.next() {
+ count += 1;
+ assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
+ }
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ assert_eq!(iter.next(), None);
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ }
+
+ assert_eq!(count, initial_len);
+ assert_eq!(list.len(), 0);
+ assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
+}
+
+#[test]
+fn drain_filter_complex() {
+ {
+ // [+xxx++++++xxxxx++++x+x++]
+ let mut list = [
+ 1, 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37,
+ 39,
+ ]
+ .into_iter()
+ .collect::<LinkedList<_>>();
+
+ let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+ assert_eq!(removed.len(), 10);
+ assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
+
+ assert_eq!(list.len(), 14);
+ assert_eq!(
+ list.into_iter().collect::<Vec<_>>(),
+ vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]
+ );
+ }
+
+ {
+ // [xxx++++++xxxxx++++x+x++]
+ let mut list =
+ [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39]
+ .into_iter()
+ .collect::<LinkedList<_>>();
+
+ let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+ assert_eq!(removed.len(), 10);
+ assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
+
+ assert_eq!(list.len(), 13);
+ assert_eq!(
+ list.into_iter().collect::<Vec<_>>(),
+ vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]
+ );
+ }
+
+ {
+ // [xxx++++++xxxxx++++x+x]
+ let mut list =
+ [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36]
+ .into_iter()
+ .collect::<LinkedList<_>>();
+
+ let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+ assert_eq!(removed.len(), 10);
+ assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
+
+ assert_eq!(list.len(), 11);
+ assert_eq!(
+ list.into_iter().collect::<Vec<_>>(),
+ vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35]
+ );
+ }
+
+ {
+ // [xxxxxxxxxx+++++++++++]
+ let mut list = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
+ .into_iter()
+ .collect::<LinkedList<_>>();
+
+ let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+ assert_eq!(removed.len(), 10);
+ assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]);
+
+ assert_eq!(list.len(), 10);
+ assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
+ }
+
+ {
+ // [+++++++++++xxxxxxxxxx]
+ let mut list = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
+ .into_iter()
+ .collect::<LinkedList<_>>();
+
+ let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+ assert_eq!(removed.len(), 10);
+ assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]);
+
+ assert_eq!(list.len(), 10);
+ assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
+ }
+}
+
+#[test]
+fn drain_filter_drop_panic_leak() {
+ static mut DROPS: i32 = 0;
+
+ struct D(bool);
+
+ impl Drop for D {
+ fn drop(&mut self) {
+ unsafe {
+ DROPS += 1;
+ }
+
+ if self.0 {
+ panic!("panic in `drop`");
+ }
+ }
+ }
+
+ let mut q = LinkedList::new();
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_front(D(false));
+ q.push_front(D(true));
+ q.push_front(D(false));
+
+ catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).ok();
+
+ assert_eq!(unsafe { DROPS }, 8);
+ assert!(q.is_empty());
+}
+
+#[test]
+fn drain_filter_pred_panic_leak() {
+ static mut DROPS: i32 = 0;
+
+ #[derive(Debug)]
+ struct D(u32);
+
+ impl Drop for D {
+ fn drop(&mut self) {
+ unsafe {
+ DROPS += 1;
+ }
+ }
+ }
+
+ let mut q = LinkedList::new();
+ q.push_back(D(3));
+ q.push_back(D(4));
+ q.push_back(D(5));
+ q.push_back(D(6));
+ q.push_back(D(7));
+ q.push_front(D(2));
+ q.push_front(D(1));
+ q.push_front(D(0));
+
+ catch_unwind(AssertUnwindSafe(|| {
+ drop(q.drain_filter(|item| if item.0 >= 2 { panic!() } else { true }))
+ }))
+ .ok();
+
+ assert_eq!(unsafe { DROPS }, 2); // 0 and 1
+ assert_eq!(q.len(), 6);
+}
+
+#[test]
+fn test_drop() {
+ static mut DROPS: i32 = 0;
+ struct Elem;
+ impl Drop for Elem {
+ fn drop(&mut self) {
+ unsafe {
+ DROPS += 1;
+ }
+ }
+ }
+
+ let mut ring = LinkedList::new();
+ ring.push_back(Elem);
+ ring.push_front(Elem);
+ ring.push_back(Elem);
+ ring.push_front(Elem);
+ drop(ring);
+
+ assert_eq!(unsafe { DROPS }, 4);
+}
+
+#[test]
+fn test_drop_with_pop() {
+ static mut DROPS: i32 = 0;
+ struct Elem;
+ impl Drop for Elem {
+ fn drop(&mut self) {
+ unsafe {
+ DROPS += 1;
+ }
+ }
+ }
+
+ let mut ring = LinkedList::new();
+ ring.push_back(Elem);
+ ring.push_front(Elem);
+ ring.push_back(Elem);
+ ring.push_front(Elem);
+
+ drop(ring.pop_back());
+ drop(ring.pop_front());
+ assert_eq!(unsafe { DROPS }, 2);
+
+ drop(ring);
+ assert_eq!(unsafe { DROPS }, 4);
+}
+
+#[test]
+fn test_drop_clear() {
+ static mut DROPS: i32 = 0;
+ struct Elem;
+ impl Drop for Elem {
+ fn drop(&mut self) {
+ unsafe {
+ DROPS += 1;
+ }
+ }
+ }
+
+ let mut ring = LinkedList::new();
+ ring.push_back(Elem);
+ ring.push_front(Elem);
+ ring.push_back(Elem);
+ ring.push_front(Elem);
+ ring.clear();
+ assert_eq!(unsafe { DROPS }, 4);
+
+ drop(ring);
+ assert_eq!(unsafe { DROPS }, 4);
+}
+
+#[test]
+fn test_drop_panic() {
+ static mut DROPS: i32 = 0;
+
+ struct D(bool);
+
+ impl Drop for D {
+ fn drop(&mut self) {
+ unsafe {
+ DROPS += 1;
+ }
+
+ if self.0 {
+ panic!("panic in `drop`");
+ }
+ }
+ }
+
+ let mut q = LinkedList::new();
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_front(D(false));
+ q.push_front(D(false));
+ q.push_front(D(true));
+
+ catch_unwind(move || drop(q)).ok();
+
+ assert_eq!(unsafe { DROPS }, 8);
+}
//!
//! 3. An asterisk `.*`:
//!
-//! `.*` means that this `{...}` is associated with *two* format inputs rather than one: the
-//! first input holds the `usize` precision, and the second holds the value to print. Note that
-//! in this case, if one uses the format string `{<arg>:<spec>.*}`, then the `<arg>` part refers
-//! to the *value* to print, and the `precision` must come in the input preceding `<arg>`.
+//! `.*` means that this `{...}` is associated with *two* format inputs rather than one:
+//! - If a format string in the fashion of `{:<spec>.*}` is used, then the first input holds
+//! the `usize` precision, and the second holds the value to print.
+//! - If a format string in the fashion of `{<arg>:<spec>.*}` is used, then the `<arg>` part
+//! refers to the value to print, and the `precision` is taken like it was specified with an
+//! omitted positional parameter (`{}` instead of `{<arg>:}`).
//!
//! For example, the following calls all print the same thing `Hello x is 0.01000`:
//!
//! // Hello {arg 0 ("x")} is {arg 2 (0.01) with precision specified in arg 1 (5)}
//! println!("Hello {0} is {2:.1$}", "x", 5, 0.01);
//!
-//! // Hello {next arg ("x")} is {second of next two args (0.01) with precision
-//! // specified in first of next two args (5)}
+//! // Hello {next arg -> arg 0 ("x")} is {second of next two args -> arg 2 (0.01) with precision
+//! // specified in first of next two args -> arg 1 (5)}
//! println!("Hello {} is {:.*}", "x", 5, 0.01);
//!
-//! // Hello {next arg ("x")} is {arg 2 (0.01) with precision
-//! // specified in its predecessor (5)}
+//! // Hello {arg 1 ("x")} is {arg 2 (0.01) with precision
+//! // specified in next arg -> arg 0 (5)}
+//! println!("Hello {1} is {2:.*}", 5, "x", 0.01);
+//!
+//! // Hello {next arg -> arg 0 ("x")} is {arg 2 (0.01) with precision
+//! // specified in next arg -> arg 1 (5)}
//! println!("Hello {} is {2:.*}", "x", 5, 0.01);
//!
-//! // Hello {next arg ("x")} is {arg "number" (0.01) with precision specified
+//! // Hello {next arg -> arg 0 ("x")} is {arg "number" (0.01) with precision specified
//! // in arg "prec" (5)}
//! println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01);
//! ```
//! ```text
//! format_string := text [ maybe_format text ] *
//! maybe_format := '{' '{' | '}' '}' | format
-//! format := '{' [ argument ] [ ':' format_spec ] '}'
+//! format := '{' [ argument ] [ ':' format_spec ] [ ws ] * '}'
//! argument := integer | identifier
//!
//! format_spec := [[fill]align][sign]['#']['0'][width]['.' precision]type
//! count := parameter | integer
//! parameter := argument '$'
//! ```
-//! In the above grammar, `text` must not contain any `'{'` or `'}'` characters.
+//! In the above grammar,
+//! - `text` must not contain any `'{'` or `'}'` characters,
+//! - `ws` is any character for which [`char::is_whitespace`] returns `true`, has no semantic
+//! meaning and is completely optional,
+//! - `integer` is a decimal integer that may contain leading zeroes and
+//! - `identifier` is an `IDENTIFIER_OR_KEYWORD` (not an `IDENTIFIER`) as defined by the [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html).
//!
//! # Formatting traits
//!
//! ```
//!
//! Your type will be passed as `self` by-reference, and then the function
-//! should emit output into the `f.buf` stream. It is up to each format trait
-//! implementation to correctly adhere to the requested formatting parameters.
-//! The values of these parameters will be listed in the fields of the
+//! should emit output into the Formatter `f` which implements `fmt::Write`. It is up to each
+//! format trait implementation to correctly adhere to the requested formatting parameters.
+//! The values of these parameters can be accessed with methods of the
//! [`Formatter`] struct. In order to help with this, the [`Formatter`] struct also
//! provides some helper methods.
//!
//!
//! ```ignore (only-for-syntax-highlight)
//! format! // described above
-//! write! // first argument is a &mut io::Write, the destination
+//! write! // first argument is either a &mut io::Write or a &mut fmt::Write, the destination
//! writeln! // same as write but appends a newline
//! print! // the format string is printed to the standard output
//! println! // same as print but appends a newline
//!
//! ### `write!`
//!
-//! This and [`writeln!`] are two macros which are used to emit the format string
+//! [`write!`] and [`writeln!`] are two macros which are used to emit the format string
//! to a specified stream. This is used to prevent intermediate allocations of
//! format strings and instead directly write the output. Under the hood, this
//! function is actually invoking the [`write_fmt`] function defined on the
-//! [`std::io::Write`] trait. Example usage is:
+//! [`std::io::Write`] and the [`std::fmt::Write`] trait. Example usage is:
//!
//! ```
//! # #![allow(unused_must_use)]
//!
//! ### `format_args!`
//!
-//! This is a curious macro used to safely pass around
+//! [`format_args!`] is a curious macro used to safely pass around
//! an opaque object describing the format string. This object
//! does not require any heap allocations to create, and it only
//! references information on the stack. Under the hood, all of
//! [`to_string`]: crate::string::ToString::to_string "ToString::to_string"
//! [`write_fmt`]: ../../std/io/trait.Write.html#method.write_fmt
//! [`std::io::Write`]: ../../std/io/trait.Write.html
+//! [`std::fmt::Write`]: ../../std/fmt/trait.Write.html
//! [`print!`]: ../../std/macro.print.html "print!"
//! [`println!`]: ../../std/macro.println.html "println!"
//! [`eprint!`]: ../../std/macro.eprint.html "eprint!"
//! [`eprintln!`]: ../../std/macro.eprintln.html "eprintln!"
+//! [`format_args!`]: ../../std/macro.format_args.html "format_args!"
//! [`fmt::Arguments`]: Arguments "fmt::Arguments"
//! [`format`]: format() "fmt::format"
/// * The first `length` bytes at `buf` need to be valid UTF-8.
///
/// Violating these may cause problems like corrupting the allocator's
- /// internal data structures.
+ /// internal data structures. For example, it is normally **not** safe to
+ /// build a `String` from a pointer to a C `char` array containing UTF-8
+ /// _unless_ you are certain that array was originally allocated by the
+ /// Rust standard library's allocator.
///
/// The ownership of `buf` is effectively transferred to the
/// `String` which may then deallocate, reallocate or change the
unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
#[inline]
fn is_zero(&self) -> bool {
- self.iter().all(IsZero::is_zero)
+ // Because this is generated as a runtime check, it's not obvious that
+ // it's worth doing if the array is really long. The threshold here
+ // is largely arbitrary, but was picked because as of 2022-05-01 LLVM
+ // can const-fold the check in `vec![[0; 32]; n]` but not in
+ // `vec![[0; 64]; n]`: https://godbolt.org/z/WTzjzfs5b
+ // Feel free to tweak if you have better evidence.
+
+ N <= 32 && self.iter().all(IsZero::is_zero)
}
}
/// * `length` needs to be less than or equal to `capacity`.
///
/// Violating these may cause problems like corrupting the allocator's
- /// internal data structures. For example it is **not** safe
- /// to build a `Vec<u8>` from a pointer to a C `char` array with length `size_t`.
+ /// internal data structures. For example it is normally **not** safe
+ /// to build a `Vec<u8>` from a pointer to a C `char` array with length
+ /// `size_t`, doing so is only safe if the array was initially allocated by
+ /// a `Vec` or `String`.
/// It's also not safe to build one from a `Vec<u16>` and its length, because
/// the allocator cares about the alignment, and these two types have different
/// alignments. The buffer was allocated with alignment 2 (for `u16`), but after
+++ /dev/null
-use std::collections::binary_heap::{Drain, PeekMut};
-use std::collections::BinaryHeap;
-use std::iter::TrustedLen;
-use std::panic::{catch_unwind, AssertUnwindSafe};
-use std::sync::atomic::{AtomicU32, Ordering};
-
-#[test]
-fn test_iterator() {
- let data = vec![5, 9, 3];
- let iterout = [9, 5, 3];
- let heap = BinaryHeap::from(data);
- let mut i = 0;
- for el in &heap {
- assert_eq!(*el, iterout[i]);
- i += 1;
- }
-}
-
-#[test]
-fn test_iter_rev_cloned_collect() {
- let data = vec![5, 9, 3];
- let iterout = vec![3, 5, 9];
- let pq = BinaryHeap::from(data);
-
- let v: Vec<_> = pq.iter().rev().cloned().collect();
- assert_eq!(v, iterout);
-}
-
-#[test]
-fn test_into_iter_collect() {
- let data = vec![5, 9, 3];
- let iterout = vec![9, 5, 3];
- let pq = BinaryHeap::from(data);
-
- let v: Vec<_> = pq.into_iter().collect();
- assert_eq!(v, iterout);
-}
-
-#[test]
-fn test_into_iter_size_hint() {
- let data = vec![5, 9];
- let pq = BinaryHeap::from(data);
-
- let mut it = pq.into_iter();
-
- assert_eq!(it.size_hint(), (2, Some(2)));
- assert_eq!(it.next(), Some(9));
-
- assert_eq!(it.size_hint(), (1, Some(1)));
- assert_eq!(it.next(), Some(5));
-
- assert_eq!(it.size_hint(), (0, Some(0)));
- assert_eq!(it.next(), None);
-}
-
-#[test]
-fn test_into_iter_rev_collect() {
- let data = vec![5, 9, 3];
- let iterout = vec![3, 5, 9];
- let pq = BinaryHeap::from(data);
-
- let v: Vec<_> = pq.into_iter().rev().collect();
- assert_eq!(v, iterout);
-}
-
-#[test]
-fn test_into_iter_sorted_collect() {
- let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
- let it = heap.into_iter_sorted();
- let sorted = it.collect::<Vec<_>>();
- assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]);
-}
-
-#[test]
-fn test_drain_sorted_collect() {
- let mut heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
- let it = heap.drain_sorted();
- let sorted = it.collect::<Vec<_>>();
- assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]);
-}
-
-fn check_exact_size_iterator<I: ExactSizeIterator>(len: usize, it: I) {
- let mut it = it;
-
- for i in 0..it.len() {
- let (lower, upper) = it.size_hint();
- assert_eq!(Some(lower), upper);
- assert_eq!(lower, len - i);
- assert_eq!(it.len(), len - i);
- it.next();
- }
- assert_eq!(it.len(), 0);
- assert!(it.is_empty());
-}
-
-#[test]
-fn test_exact_size_iterator() {
- let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
- check_exact_size_iterator(heap.len(), heap.iter());
- check_exact_size_iterator(heap.len(), heap.clone().into_iter());
- check_exact_size_iterator(heap.len(), heap.clone().into_iter_sorted());
- check_exact_size_iterator(heap.len(), heap.clone().drain());
- check_exact_size_iterator(heap.len(), heap.clone().drain_sorted());
-}
-
-fn check_trusted_len<I: TrustedLen>(len: usize, it: I) {
- let mut it = it;
- for i in 0..len {
- let (lower, upper) = it.size_hint();
- if upper.is_some() {
- assert_eq!(Some(lower), upper);
- assert_eq!(lower, len - i);
- }
- it.next();
- }
-}
-
-#[test]
-fn test_trusted_len() {
- let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
- check_trusted_len(heap.len(), heap.clone().into_iter_sorted());
- check_trusted_len(heap.len(), heap.clone().drain_sorted());
-}
-
-#[test]
-fn test_peek_and_pop() {
- let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
- let mut sorted = data.clone();
- sorted.sort();
- let mut heap = BinaryHeap::from(data);
- while !heap.is_empty() {
- assert_eq!(heap.peek().unwrap(), sorted.last().unwrap());
- assert_eq!(heap.pop().unwrap(), sorted.pop().unwrap());
- }
-}
-
-#[test]
-fn test_peek_mut() {
- let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
- let mut heap = BinaryHeap::from(data);
- assert_eq!(heap.peek(), Some(&10));
- {
- let mut top = heap.peek_mut().unwrap();
- *top -= 2;
- }
- assert_eq!(heap.peek(), Some(&9));
-}
-
-#[test]
-fn test_peek_mut_pop() {
- let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
- let mut heap = BinaryHeap::from(data);
- assert_eq!(heap.peek(), Some(&10));
- {
- let mut top = heap.peek_mut().unwrap();
- *top -= 2;
- assert_eq!(PeekMut::pop(top), 8);
- }
- assert_eq!(heap.peek(), Some(&9));
-}
-
-#[test]
-fn test_push() {
- let mut heap = BinaryHeap::from(vec![2, 4, 9]);
- assert_eq!(heap.len(), 3);
- assert!(*heap.peek().unwrap() == 9);
- heap.push(11);
- assert_eq!(heap.len(), 4);
- assert!(*heap.peek().unwrap() == 11);
- heap.push(5);
- assert_eq!(heap.len(), 5);
- assert!(*heap.peek().unwrap() == 11);
- heap.push(27);
- assert_eq!(heap.len(), 6);
- assert!(*heap.peek().unwrap() == 27);
- heap.push(3);
- assert_eq!(heap.len(), 7);
- assert!(*heap.peek().unwrap() == 27);
- heap.push(103);
- assert_eq!(heap.len(), 8);
- assert!(*heap.peek().unwrap() == 103);
-}
-
-#[test]
-fn test_push_unique() {
- let mut heap = BinaryHeap::<Box<_>>::from(vec![box 2, box 4, box 9]);
- assert_eq!(heap.len(), 3);
- assert!(**heap.peek().unwrap() == 9);
- heap.push(box 11);
- assert_eq!(heap.len(), 4);
- assert!(**heap.peek().unwrap() == 11);
- heap.push(box 5);
- assert_eq!(heap.len(), 5);
- assert!(**heap.peek().unwrap() == 11);
- heap.push(box 27);
- assert_eq!(heap.len(), 6);
- assert!(**heap.peek().unwrap() == 27);
- heap.push(box 3);
- assert_eq!(heap.len(), 7);
- assert!(**heap.peek().unwrap() == 27);
- heap.push(box 103);
- assert_eq!(heap.len(), 8);
- assert!(**heap.peek().unwrap() == 103);
-}
-
-fn check_to_vec(mut data: Vec<i32>) {
- let heap = BinaryHeap::from(data.clone());
- let mut v = heap.clone().into_vec();
- v.sort();
- data.sort();
-
- assert_eq!(v, data);
- assert_eq!(heap.into_sorted_vec(), data);
-}
-
-#[test]
-fn test_to_vec() {
- check_to_vec(vec![]);
- check_to_vec(vec![5]);
- check_to_vec(vec![3, 2]);
- check_to_vec(vec![2, 3]);
- check_to_vec(vec![5, 1, 2]);
- check_to_vec(vec![1, 100, 2, 3]);
- check_to_vec(vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 0]);
- check_to_vec(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
- check_to_vec(vec![9, 11, 9, 9, 9, 9, 11, 2, 3, 4, 11, 9, 0, 0, 0, 0]);
- check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
- check_to_vec(vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]);
- check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 1, 2]);
- check_to_vec(vec![5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1]);
-}
-
-#[test]
-fn test_in_place_iterator_specialization() {
- let src: Vec<usize> = vec![1, 2, 3];
- let src_ptr = src.as_ptr();
- let heap: BinaryHeap<_> = src.into_iter().map(std::convert::identity).collect();
- let heap_ptr = heap.iter().next().unwrap() as *const usize;
- assert_eq!(src_ptr, heap_ptr);
- let sink: Vec<_> = heap.into_iter().map(std::convert::identity).collect();
- let sink_ptr = sink.as_ptr();
- assert_eq!(heap_ptr, sink_ptr);
-}
-
-#[test]
-fn test_empty_pop() {
- let mut heap = BinaryHeap::<i32>::new();
- assert!(heap.pop().is_none());
-}
-
-#[test]
-fn test_empty_peek() {
- let empty = BinaryHeap::<i32>::new();
- assert!(empty.peek().is_none());
-}
-
-#[test]
-fn test_empty_peek_mut() {
- let mut empty = BinaryHeap::<i32>::new();
- assert!(empty.peek_mut().is_none());
-}
-
-#[test]
-fn test_from_iter() {
- let xs = vec![9, 8, 7, 6, 5, 4, 3, 2, 1];
-
- let mut q: BinaryHeap<_> = xs.iter().rev().cloned().collect();
-
- for &x in &xs {
- assert_eq!(q.pop().unwrap(), x);
- }
-}
-
-#[test]
-fn test_drain() {
- let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect();
-
- assert_eq!(q.drain().take(5).count(), 5);
-
- assert!(q.is_empty());
-}
-
-#[test]
-fn test_drain_sorted() {
- let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect();
-
- assert_eq!(q.drain_sorted().take(5).collect::<Vec<_>>(), vec![9, 8, 7, 6, 5]);
-
- assert!(q.is_empty());
-}
-
-#[test]
-fn test_drain_sorted_leak() {
- static DROPS: AtomicU32 = AtomicU32::new(0);
-
- #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
- struct D(u32, bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- DROPS.fetch_add(1, Ordering::SeqCst);
-
- if self.1 {
- panic!("panic in `drop`");
- }
- }
- }
-
- let mut q = BinaryHeap::from(vec![
- D(0, false),
- D(1, false),
- D(2, false),
- D(3, true),
- D(4, false),
- D(5, false),
- ]);
-
- catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).ok();
-
- assert_eq!(DROPS.load(Ordering::SeqCst), 6);
-}
-
-#[test]
-fn test_extend_ref() {
- let mut a = BinaryHeap::new();
- a.push(1);
- a.push(2);
-
- a.extend(&[3, 4, 5]);
-
- assert_eq!(a.len(), 5);
- assert_eq!(a.into_sorted_vec(), [1, 2, 3, 4, 5]);
-
- let mut a = BinaryHeap::new();
- a.push(1);
- a.push(2);
- let mut b = BinaryHeap::new();
- b.push(3);
- b.push(4);
- b.push(5);
-
- a.extend(&b);
-
- assert_eq!(a.len(), 5);
- assert_eq!(a.into_sorted_vec(), [1, 2, 3, 4, 5]);
-}
-
-#[test]
-fn test_append() {
- let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]);
- let mut b = BinaryHeap::from(vec![-20, 5, 43]);
-
- a.append(&mut b);
-
- assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]);
- assert!(b.is_empty());
-}
-
-#[test]
-fn test_append_to_empty() {
- let mut a = BinaryHeap::new();
- let mut b = BinaryHeap::from(vec![-20, 5, 43]);
-
- a.append(&mut b);
-
- assert_eq!(a.into_sorted_vec(), [-20, 5, 43]);
- assert!(b.is_empty());
-}
-
-#[test]
-fn test_extend_specialization() {
- let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]);
- let b = BinaryHeap::from(vec![-20, 5, 43]);
-
- a.extend(b);
-
- assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]);
-}
-
-#[allow(dead_code)]
-fn assert_covariance() {
- fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> {
- d
- }
-}
-
-#[test]
-fn test_retain() {
- let mut a = BinaryHeap::from(vec![100, 10, 50, 1, 2, 20, 30]);
- a.retain(|&x| x != 2);
-
- // Check that 20 moved into 10's place.
- assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]);
-
- a.retain(|_| true);
-
- assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]);
-
- a.retain(|&x| x < 50);
-
- assert_eq!(a.clone().into_vec(), [30, 20, 10, 1]);
-
- a.retain(|_| false);
-
- assert!(a.is_empty());
-}
-
-// old binaryheap failed this test
-//
-// Integrity means that all elements are present after a comparison panics,
-// even if the order might not be correct.
-//
-// Destructors must be called exactly once per element.
-// FIXME: re-enable emscripten once it can unwind again
-#[test]
-#[cfg(not(target_os = "emscripten"))]
-fn panic_safe() {
- use rand::{seq::SliceRandom, thread_rng};
- use std::cmp;
- use std::panic::{self, AssertUnwindSafe};
- use std::sync::atomic::{AtomicUsize, Ordering};
-
- static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
-
- #[derive(Eq, PartialEq, Ord, Clone, Debug)]
- struct PanicOrd<T>(T, bool);
-
- impl<T> Drop for PanicOrd<T> {
- fn drop(&mut self) {
- // update global drop count
- DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
- }
- }
-
- impl<T: PartialOrd> PartialOrd for PanicOrd<T> {
- fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
- if self.1 || other.1 {
- panic!("Panicking comparison");
- }
- self.0.partial_cmp(&other.0)
- }
- }
- let mut rng = thread_rng();
- const DATASZ: usize = 32;
- // Miri is too slow
- let ntest = if cfg!(miri) { 1 } else { 10 };
-
- // don't use 0 in the data -- we want to catch the zeroed-out case.
- let data = (1..=DATASZ).collect::<Vec<_>>();
-
- // since it's a fuzzy test, run several tries.
- for _ in 0..ntest {
- for i in 1..=DATASZ {
- DROP_COUNTER.store(0, Ordering::SeqCst);
-
- let mut panic_ords: Vec<_> =
- data.iter().filter(|&&x| x != i).map(|&x| PanicOrd(x, false)).collect();
- let panic_item = PanicOrd(i, true);
-
- // heapify the sane items
- panic_ords.shuffle(&mut rng);
- let mut heap = BinaryHeap::from(panic_ords);
- let inner_data;
-
- {
- // push the panicking item to the heap and catch the panic
- let thread_result = {
- let mut heap_ref = AssertUnwindSafe(&mut heap);
- panic::catch_unwind(move || {
- heap_ref.push(panic_item);
- })
- };
- assert!(thread_result.is_err());
-
- // Assert no elements were dropped
- let drops = DROP_COUNTER.load(Ordering::SeqCst);
- assert!(drops == 0, "Must not drop items. drops={}", drops);
- inner_data = heap.clone().into_vec();
- drop(heap);
- }
- let drops = DROP_COUNTER.load(Ordering::SeqCst);
- assert_eq!(drops, DATASZ);
-
- let mut data_sorted = inner_data.into_iter().map(|p| p.0).collect::<Vec<_>>();
- data_sorted.sort();
- assert_eq!(data_sorted, data);
- }
- }
-}
use std::hash::{Hash, Hasher};
mod arc;
-mod binary_heap;
mod borrow;
mod boxed;
mod btree_set_hash;
use std::collections::LinkedList;
-use std::panic::{catch_unwind, AssertUnwindSafe};
-
-#[test]
-fn test_basic() {
- let mut m = LinkedList::<Box<_>>::new();
- assert_eq!(m.pop_front(), None);
- assert_eq!(m.pop_back(), None);
- assert_eq!(m.pop_front(), None);
- m.push_front(box 1);
- assert_eq!(m.pop_front(), Some(box 1));
- m.push_back(box 2);
- m.push_back(box 3);
- assert_eq!(m.len(), 2);
- assert_eq!(m.pop_front(), Some(box 2));
- assert_eq!(m.pop_front(), Some(box 3));
- assert_eq!(m.len(), 0);
- assert_eq!(m.pop_front(), None);
- m.push_back(box 1);
- m.push_back(box 3);
- m.push_back(box 5);
- m.push_back(box 7);
- assert_eq!(m.pop_front(), Some(box 1));
-
- let mut n = LinkedList::new();
- n.push_front(2);
- n.push_front(3);
- {
- assert_eq!(n.front().unwrap(), &3);
- let x = n.front_mut().unwrap();
- assert_eq!(*x, 3);
- *x = 0;
- }
- {
- assert_eq!(n.back().unwrap(), &2);
- let y = n.back_mut().unwrap();
- assert_eq!(*y, 2);
- *y = 1;
- }
- assert_eq!(n.pop_front(), Some(0));
- assert_eq!(n.pop_front(), Some(1));
-}
-
-fn generate_test() -> LinkedList<i32> {
- list_from(&[0, 1, 2, 3, 4, 5, 6])
-}
-
-fn list_from<T: Clone>(v: &[T]) -> LinkedList<T> {
- v.iter().cloned().collect()
-}
-
-#[test]
-fn test_split_off() {
- // singleton
- {
- let mut m = LinkedList::new();
- m.push_back(1);
-
- let p = m.split_off(0);
- assert_eq!(m.len(), 0);
- assert_eq!(p.len(), 1);
- assert_eq!(p.back(), Some(&1));
- assert_eq!(p.front(), Some(&1));
- }
-
- // not singleton, forwards
- {
- let u = vec![1, 2, 3, 4, 5];
- let mut m = list_from(&u);
- let mut n = m.split_off(2);
- assert_eq!(m.len(), 2);
- assert_eq!(n.len(), 3);
- for elt in 1..3 {
- assert_eq!(m.pop_front(), Some(elt));
- }
- for elt in 3..6 {
- assert_eq!(n.pop_front(), Some(elt));
- }
- }
- // not singleton, backwards
- {
- let u = vec![1, 2, 3, 4, 5];
- let mut m = list_from(&u);
- let mut n = m.split_off(4);
- assert_eq!(m.len(), 4);
- assert_eq!(n.len(), 1);
- for elt in 1..5 {
- assert_eq!(m.pop_front(), Some(elt));
- }
- for elt in 5..6 {
- assert_eq!(n.pop_front(), Some(elt));
- }
- }
-
- // no-op on the last index
- {
- let mut m = LinkedList::new();
- m.push_back(1);
-
- let p = m.split_off(1);
- assert_eq!(m.len(), 1);
- assert_eq!(p.len(), 0);
- assert_eq!(m.back(), Some(&1));
- assert_eq!(m.front(), Some(&1));
- }
-}
-
-#[test]
-fn test_iterator() {
- let m = generate_test();
- for (i, elt) in m.iter().enumerate() {
- assert_eq!(i as i32, *elt);
- }
- let mut n = LinkedList::new();
- assert_eq!(n.iter().next(), None);
- n.push_front(4);
- let mut it = n.iter();
- assert_eq!(it.size_hint(), (1, Some(1)));
- assert_eq!(it.next().unwrap(), &4);
- assert_eq!(it.size_hint(), (0, Some(0)));
- assert_eq!(it.next(), None);
-}
-
-#[test]
-fn test_iterator_clone() {
- let mut n = LinkedList::new();
- n.push_back(2);
- n.push_back(3);
- n.push_back(4);
- let mut it = n.iter();
- it.next();
- let mut jt = it.clone();
- assert_eq!(it.next(), jt.next());
- assert_eq!(it.next_back(), jt.next_back());
- assert_eq!(it.next(), jt.next());
-}
-
-#[test]
-fn test_iterator_double_end() {
- let mut n = LinkedList::new();
- assert_eq!(n.iter().next(), None);
- n.push_front(4);
- n.push_front(5);
- n.push_front(6);
- let mut it = n.iter();
- assert_eq!(it.size_hint(), (3, Some(3)));
- assert_eq!(it.next().unwrap(), &6);
- assert_eq!(it.size_hint(), (2, Some(2)));
- assert_eq!(it.next_back().unwrap(), &4);
- assert_eq!(it.size_hint(), (1, Some(1)));
- assert_eq!(it.next_back().unwrap(), &5);
- assert_eq!(it.next_back(), None);
- assert_eq!(it.next(), None);
-}
-
-#[test]
-fn test_rev_iter() {
- let m = generate_test();
- for (i, elt) in m.iter().rev().enumerate() {
- assert_eq!((6 - i) as i32, *elt);
- }
- let mut n = LinkedList::new();
- assert_eq!(n.iter().rev().next(), None);
- n.push_front(4);
- let mut it = n.iter().rev();
- assert_eq!(it.size_hint(), (1, Some(1)));
- assert_eq!(it.next().unwrap(), &4);
- assert_eq!(it.size_hint(), (0, Some(0)));
- assert_eq!(it.next(), None);
-}
-
-#[test]
-fn test_mut_iter() {
- let mut m = generate_test();
- let mut len = m.len();
- for (i, elt) in m.iter_mut().enumerate() {
- assert_eq!(i as i32, *elt);
- len -= 1;
- }
- assert_eq!(len, 0);
- let mut n = LinkedList::new();
- assert!(n.iter_mut().next().is_none());
- n.push_front(4);
- n.push_back(5);
- let mut it = n.iter_mut();
- assert_eq!(it.size_hint(), (2, Some(2)));
- assert!(it.next().is_some());
- assert!(it.next().is_some());
- assert_eq!(it.size_hint(), (0, Some(0)));
- assert!(it.next().is_none());
-}
-
-#[test]
-fn test_iterator_mut_double_end() {
- let mut n = LinkedList::new();
- assert!(n.iter_mut().next_back().is_none());
- n.push_front(4);
- n.push_front(5);
- n.push_front(6);
- let mut it = n.iter_mut();
- assert_eq!(it.size_hint(), (3, Some(3)));
- assert_eq!(*it.next().unwrap(), 6);
- assert_eq!(it.size_hint(), (2, Some(2)));
- assert_eq!(*it.next_back().unwrap(), 4);
- assert_eq!(it.size_hint(), (1, Some(1)));
- assert_eq!(*it.next_back().unwrap(), 5);
- assert!(it.next_back().is_none());
- assert!(it.next().is_none());
-}
-
-#[test]
-fn test_mut_rev_iter() {
- let mut m = generate_test();
- for (i, elt) in m.iter_mut().rev().enumerate() {
- assert_eq!((6 - i) as i32, *elt);
- }
- let mut n = LinkedList::new();
- assert!(n.iter_mut().rev().next().is_none());
- n.push_front(4);
- let mut it = n.iter_mut().rev();
- assert!(it.next().is_some());
- assert!(it.next().is_none());
-}
-
-#[test]
-fn test_eq() {
- let mut n = list_from(&[]);
- let mut m = list_from(&[]);
- assert!(n == m);
- n.push_front(1);
- assert!(n != m);
- m.push_back(1);
- assert!(n == m);
-
- let n = list_from(&[2, 3, 4]);
- let m = list_from(&[1, 2, 3]);
- assert!(n != m);
-}
#[test]
fn test_hash() {
assert!(hash(&x) == hash(&y));
}
-
-#[test]
-fn test_ord() {
- let n = list_from(&[]);
- let m = list_from(&[1, 2, 3]);
- assert!(n < m);
- assert!(m > n);
- assert!(n <= n);
- assert!(n >= n);
-}
-
-#[test]
-fn test_ord_nan() {
- let nan = 0.0f64 / 0.0;
- let n = list_from(&[nan]);
- let m = list_from(&[nan]);
- assert!(!(n < m));
- assert!(!(n > m));
- assert!(!(n <= m));
- assert!(!(n >= m));
-
- let n = list_from(&[nan]);
- let one = list_from(&[1.0f64]);
- assert!(!(n < one));
- assert!(!(n > one));
- assert!(!(n <= one));
- assert!(!(n >= one));
-
- let u = list_from(&[1.0f64, 2.0, nan]);
- let v = list_from(&[1.0f64, 2.0, 3.0]);
- assert!(!(u < v));
- assert!(!(u > v));
- assert!(!(u <= v));
- assert!(!(u >= v));
-
- let s = list_from(&[1.0f64, 2.0, 4.0, 2.0]);
- let t = list_from(&[1.0f64, 2.0, 3.0, 2.0]);
- assert!(!(s < t));
- assert!(s > one);
- assert!(!(s <= one));
- assert!(s >= one);
-}
-
-#[test]
-fn test_show() {
- let list: LinkedList<_> = (0..10).collect();
- assert_eq!(format!("{list:?}"), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
-
- let list: LinkedList<_> = ["just", "one", "test", "more"].into_iter().collect();
- assert_eq!(format!("{list:?}"), "[\"just\", \"one\", \"test\", \"more\"]");
-}
-
-#[test]
-fn test_extend_ref() {
- let mut a = LinkedList::new();
- a.push_back(1);
-
- a.extend(&[2, 3, 4]);
-
- assert_eq!(a.len(), 4);
- assert_eq!(a, list_from(&[1, 2, 3, 4]));
-
- let mut b = LinkedList::new();
- b.push_back(5);
- b.push_back(6);
- a.extend(&b);
-
- assert_eq!(a.len(), 6);
- assert_eq!(a, list_from(&[1, 2, 3, 4, 5, 6]));
-}
-
-#[test]
-fn test_extend() {
- let mut a = LinkedList::new();
- a.push_back(1);
- a.extend(vec![2, 3, 4]); // uses iterator
-
- assert_eq!(a.len(), 4);
- assert!(a.iter().eq(&[1, 2, 3, 4]));
-
- let b: LinkedList<_> = [5, 6, 7].into_iter().collect();
- a.extend(b); // specializes to `append`
-
- assert_eq!(a.len(), 7);
- assert!(a.iter().eq(&[1, 2, 3, 4, 5, 6, 7]));
-}
-
-#[test]
-fn test_contains() {
- let mut l = LinkedList::new();
- l.extend(&[2, 3, 4]);
-
- assert!(l.contains(&3));
- assert!(!l.contains(&1));
-
- l.clear();
-
- assert!(!l.contains(&3));
-}
-
-#[test]
-fn drain_filter_empty() {
- let mut list: LinkedList<i32> = LinkedList::new();
-
- {
- let mut iter = list.drain_filter(|_| true);
- assert_eq!(iter.size_hint(), (0, Some(0)));
- assert_eq!(iter.next(), None);
- assert_eq!(iter.size_hint(), (0, Some(0)));
- assert_eq!(iter.next(), None);
- assert_eq!(iter.size_hint(), (0, Some(0)));
- }
-
- assert_eq!(list.len(), 0);
- assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
-}
-
-#[test]
-fn drain_filter_zst() {
- let mut list: LinkedList<_> = [(), (), (), (), ()].into_iter().collect();
- let initial_len = list.len();
- let mut count = 0;
-
- {
- let mut iter = list.drain_filter(|_| true);
- assert_eq!(iter.size_hint(), (0, Some(initial_len)));
- while let Some(_) = iter.next() {
- count += 1;
- assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
- }
- assert_eq!(iter.size_hint(), (0, Some(0)));
- assert_eq!(iter.next(), None);
- assert_eq!(iter.size_hint(), (0, Some(0)));
- }
-
- assert_eq!(count, initial_len);
- assert_eq!(list.len(), 0);
- assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
-}
-
-#[test]
-fn drain_filter_false() {
- let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
-
- let initial_len = list.len();
- let mut count = 0;
-
- {
- let mut iter = list.drain_filter(|_| false);
- assert_eq!(iter.size_hint(), (0, Some(initial_len)));
- for _ in iter.by_ref() {
- count += 1;
- }
- assert_eq!(iter.size_hint(), (0, Some(0)));
- assert_eq!(iter.next(), None);
- assert_eq!(iter.size_hint(), (0, Some(0)));
- }
-
- assert_eq!(count, 0);
- assert_eq!(list.len(), initial_len);
- assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
-}
-
-#[test]
-fn drain_filter_true() {
- let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
-
- let initial_len = list.len();
- let mut count = 0;
-
- {
- let mut iter = list.drain_filter(|_| true);
- assert_eq!(iter.size_hint(), (0, Some(initial_len)));
- while let Some(_) = iter.next() {
- count += 1;
- assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
- }
- assert_eq!(iter.size_hint(), (0, Some(0)));
- assert_eq!(iter.next(), None);
- assert_eq!(iter.size_hint(), (0, Some(0)));
- }
-
- assert_eq!(count, initial_len);
- assert_eq!(list.len(), 0);
- assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
-}
-
-#[test]
-fn drain_filter_complex() {
- {
- // [+xxx++++++xxxxx++++x+x++]
- let mut list = [
- 1, 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37,
- 39,
- ]
- .into_iter()
- .collect::<LinkedList<_>>();
-
- let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
- assert_eq!(removed.len(), 10);
- assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
-
- assert_eq!(list.len(), 14);
- assert_eq!(
- list.into_iter().collect::<Vec<_>>(),
- vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]
- );
- }
-
- {
- // [xxx++++++xxxxx++++x+x++]
- let mut list =
- [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39]
- .into_iter()
- .collect::<LinkedList<_>>();
-
- let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
- assert_eq!(removed.len(), 10);
- assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
-
- assert_eq!(list.len(), 13);
- assert_eq!(
- list.into_iter().collect::<Vec<_>>(),
- vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]
- );
- }
-
- {
- // [xxx++++++xxxxx++++x+x]
- let mut list =
- [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36]
- .into_iter()
- .collect::<LinkedList<_>>();
-
- let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
- assert_eq!(removed.len(), 10);
- assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
-
- assert_eq!(list.len(), 11);
- assert_eq!(
- list.into_iter().collect::<Vec<_>>(),
- vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35]
- );
- }
-
- {
- // [xxxxxxxxxx+++++++++++]
- let mut list = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
- .into_iter()
- .collect::<LinkedList<_>>();
-
- let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
- assert_eq!(removed.len(), 10);
- assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]);
-
- assert_eq!(list.len(), 10);
- assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
- }
-
- {
- // [+++++++++++xxxxxxxxxx]
- let mut list = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
- .into_iter()
- .collect::<LinkedList<_>>();
-
- let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
- assert_eq!(removed.len(), 10);
- assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]);
-
- assert_eq!(list.len(), 10);
- assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
- }
-}
-
-#[test]
-fn drain_filter_drop_panic_leak() {
- static mut DROPS: i32 = 0;
-
- struct D(bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
-
- if self.0 {
- panic!("panic in `drop`");
- }
- }
- }
-
- let mut q = LinkedList::new();
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_front(D(false));
- q.push_front(D(true));
- q.push_front(D(false));
-
- catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).ok();
-
- assert_eq!(unsafe { DROPS }, 8);
- assert!(q.is_empty());
-}
-
-#[test]
-fn drain_filter_pred_panic_leak() {
- static mut DROPS: i32 = 0;
-
- #[derive(Debug)]
- struct D(u32);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
-
- let mut q = LinkedList::new();
- q.push_back(D(3));
- q.push_back(D(4));
- q.push_back(D(5));
- q.push_back(D(6));
- q.push_back(D(7));
- q.push_front(D(2));
- q.push_front(D(1));
- q.push_front(D(0));
-
- catch_unwind(AssertUnwindSafe(|| {
- drop(q.drain_filter(|item| if item.0 >= 2 { panic!() } else { true }))
- }))
- .ok();
-
- assert_eq!(unsafe { DROPS }, 2); // 0 and 1
- assert_eq!(q.len(), 6);
-}
-
-#[test]
-fn test_drop() {
- static mut DROPS: i32 = 0;
- struct Elem;
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
-
- let mut ring = LinkedList::new();
- ring.push_back(Elem);
- ring.push_front(Elem);
- ring.push_back(Elem);
- ring.push_front(Elem);
- drop(ring);
-
- assert_eq!(unsafe { DROPS }, 4);
-}
-
-#[test]
-fn test_drop_with_pop() {
- static mut DROPS: i32 = 0;
- struct Elem;
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
-
- let mut ring = LinkedList::new();
- ring.push_back(Elem);
- ring.push_front(Elem);
- ring.push_back(Elem);
- ring.push_front(Elem);
-
- drop(ring.pop_back());
- drop(ring.pop_front());
- assert_eq!(unsafe { DROPS }, 2);
-
- drop(ring);
- assert_eq!(unsafe { DROPS }, 4);
-}
-
-#[test]
-fn test_drop_clear() {
- static mut DROPS: i32 = 0;
- struct Elem;
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
-
- let mut ring = LinkedList::new();
- ring.push_back(Elem);
- ring.push_front(Elem);
- ring.push_back(Elem);
- ring.push_front(Elem);
- ring.clear();
- assert_eq!(unsafe { DROPS }, 4);
-
- drop(ring);
- assert_eq!(unsafe { DROPS }, 4);
-}
-
-#[test]
-fn test_drop_panic() {
- static mut DROPS: i32 = 0;
-
- struct D(bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
-
- if self.0 {
- panic!("panic in `drop`");
- }
- }
- }
-
- let mut q = LinkedList::new();
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_front(D(false));
- q.push_front(D(false));
- q.push_front(D(true));
-
- catch_unwind(move || drop(q)).ok();
-
- assert_eq!(unsafe { DROPS }, 8);
-}
fn bench_lt(b: &mut Bencher) {
b.iter(|| (0..100000).map(black_box).lt((0..100000).map(black_box)))
}
+
+#[bench]
+fn bench_trusted_random_access_adapters(b: &mut Bencher) {
+ let vec1: Vec<_> = (0usize..100000).collect();
+ let vec2 = black_box(vec1.clone());
+ b.iter(|| {
+ let mut iter = vec1
+ .iter()
+ .copied()
+ .enumerate()
+ .map(|(idx, e)| idx.wrapping_add(e))
+ .zip(vec2.iter().copied())
+ .map(|(a, b)| a.wrapping_add(b))
+ .fuse();
+ let mut acc: usize = 0;
+ let size = iter.size();
+ for i in 0..size {
+ // SAFETY: TRA requirements are satisfied by 0..size iteration and then dropping the
+ // iterator.
+ acc = acc.wrapping_add(unsafe { iter.__iterator_get_unchecked(i) });
+ }
+ acc
+ })
+}
#![feature(flt2dec)]
#![feature(int_log)]
#![feature(test)]
+#![feature(trusted_random_access)]
extern crate test;
// is fixed.
#[cfg(all())]
#[doc(cfg(all()))] }
+
type_alias! { "c_schar.md", c_schar = i8, NonZero_c_schar = NonZeroI8; }
type_alias! { "c_uchar.md", c_uchar = u8, NonZero_c_uchar = NonZeroU8; }
type_alias! { "c_short.md", c_short = i16, NonZero_c_short = NonZeroI16; }
type_alias! { "c_ushort.md", c_ushort = u16, NonZero_c_ushort = NonZeroU16; }
-#[cfg(any(target_arch = "avr", target_arch = "msp430"))]
-type_alias! { "c_int.md", c_int = i16, NonZero_c_int = NonZeroI16; }
-#[cfg(not(any(target_arch = "avr", target_arch = "msp430")))]
-type_alias! { "c_int.md", c_int = i32, NonZero_c_int = NonZeroI32; }
-type_alias! { "c_uint.md", c_uint = u32, NonZero_c_uint = NonZeroU32; }
-type_alias! { "c_long.md", c_long = i32, NonZero_c_long = NonZeroI32;
-#[doc(cfg(all()))]
-#[cfg(any(target_pointer_width = "32", windows))] }
-type_alias! { "c_ulong.md", c_ulong = u32, NonZero_c_ulong = NonZeroU32;
-#[doc(cfg(all()))]
-#[cfg(any(target_pointer_width = "32", windows))] }
-type_alias! { "c_long.md", c_long = i64, NonZero_c_long = NonZeroI64;
-#[doc(cfg(all()))]
-#[cfg(all(target_pointer_width = "64", not(windows)))] }
-type_alias! { "c_ulong.md", c_ulong = u64, NonZero_c_ulong = NonZeroU64;
-#[doc(cfg(all()))]
-#[cfg(all(target_pointer_width = "64", not(windows)))] }
+
+type_alias! { "c_int.md", c_int = c_int_definition::c_int, NonZero_c_int = c_int_definition::NonZero_c_int;
+#[doc(cfg(all()))] }
+type_alias! { "c_uint.md", c_uint = c_int_definition::c_uint, NonZero_c_uint = c_int_definition::NonZero_c_uint;
+#[doc(cfg(all()))] }
+
+type_alias! { "c_long.md", c_long = c_long_definition::c_long, NonZero_c_long = c_long_definition::NonZero_c_long;
+#[doc(cfg(all()))] }
+type_alias! { "c_ulong.md", c_ulong = c_long_definition::c_ulong, NonZero_c_ulong = c_long_definition::NonZero_c_ulong;
+#[doc(cfg(all()))] }
+
type_alias! { "c_longlong.md", c_longlong = i64, NonZero_c_longlong = NonZeroI64; }
type_alias! { "c_ulonglong.md", c_ulonglong = u64, NonZero_c_ulonglong = NonZeroU64; }
+
type_alias_no_nz! { "c_float.md", c_float = f32; }
type_alias_no_nz! { "c_double.md", c_double = f64; }
}
}
+mod c_int_definition {
+ cfg_if! {
+ if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] {
+ pub type c_int = i16;
+ pub type NonZero_c_int = crate::num::NonZeroI16;
+ pub type c_uint = u16;
+ pub type NonZero_c_uint = crate::num::NonZeroU16;
+ } else {
+ pub type c_int = i32;
+ pub type NonZero_c_int = crate::num::NonZeroI32;
+ pub type c_uint = u32;
+ pub type NonZero_c_uint = crate::num::NonZeroU32;
+ }
+ }
+}
+
+mod c_long_definition {
+ cfg_if! {
+ if #[cfg(all(target_pointer_width = "64", not(windows)))] {
+ pub type c_long = i64;
+ pub type NonZero_c_long = crate::num::NonZeroI64;
+ pub type c_ulong = u64;
+ pub type NonZero_c_ulong = crate::num::NonZeroU64;
+ } else {
+ // The minimal size of `long` in the C standard is 32 bits
+ pub type c_long = i32;
+ pub type NonZero_c_long = crate::num::NonZeroI32;
+ pub type c_ulong = u32;
+ pub type NonZero_c_ulong = crate::num::NonZeroU32;
+ }
+ }
+}
+
// N.B., for LLVM to recognize the void pointer type and by extension
// functions like malloc(), we need to have it represented as i8* in
// LLVM bitcode. The enum used here ensures this and prevents misuse
#[rustc_inherit_overflow_checks]
#[doc(hidden)]
+ #[inline]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> <Self as Iterator>::Item
where
Self: TrustedRandomAccessNoCoerce,
}
#[doc(hidden)]
+ #[inline]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> B
where
Self: TrustedRandomAccessNoCoerce,
///
/// Same requirements calling `get_unchecked` directly.
#[doc(hidden)]
+#[inline]
pub(in crate::iter::adapters) unsafe fn try_get_unchecked<I>(it: &mut I, idx: usize) -> I::Item
where
I: Iterator,
}
unsafe impl<I: Iterator + TrustedRandomAccessNoCoerce> SpecTrustedRandomAccess for I {
+ #[inline]
unsafe fn try_get_unchecked(&mut self, index: usize) -> Self::Item {
// SAFETY: the caller must uphold the contract for
// `Iterator::__iterator_get_unchecked`.
///
/// Like `panic!`, this macro has a second form for displaying custom values.
///
+/// [`todo!`]: crate::todo
+///
/// # Examples
///
/// Say we have a trait `Foo`:
///
/// # Panics
///
- /// When the number is zero, or if the base is not at least 2; it
+ /// When the number is negative, zero, or if the base is not at least 2; it
/// panics in debug mode and the return value is 0 in release
/// mode.
///
///
/// # Panics
///
- /// When the number is zero it panics in debug mode and the return value
+ /// When the number is negative or zero it panics in debug mode and the return value
/// is 0 in release mode.
///
/// # Examples
///
/// # Panics
///
- /// When the number is zero it panics in debug mode and the return value
+ /// When the number is negative or zero it panics in debug mode and the return value
/// is 0 in release mode.
///
/// # Example
///
/// # Panics
///
- /// When the number is negative, zero, or if the base is not at least 2;
+ /// When the number is zero, or if the base is not at least 2;
/// it panics in debug mode and the return value is 0 in release mode.
///
/// # Examples
///
/// # Panics
///
- /// When the number is negative or zero it panics in debug mode and
+ /// When the number is zero it panics in debug mode and
/// the return value is 0 in release mode.
///
/// # Examples
///
/// # Panics
///
- /// When the number is negative or zero it panics in debug mode and the
+ /// When the number is zero it panics in debug mode and the
/// return value is 0 in release mode.
///
/// # Example
#[unstable(feature = "try_trait_v2", issue = "84277")]
pub use self::try_trait::{FromResidual, Try};
+#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
+pub use self::try_trait::Yeet;
+
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
pub use self::try_trait::Residual;
fn from_residual(residual: R) -> Self;
}
+#[cfg(not(bootstrap))]
+#[unstable(
+ feature = "yeet_desugar_details",
+ issue = "none",
+ reason = "just here to simplify the desugaring; will never be stabilized"
+)]
+#[inline]
+#[track_caller] // because `Result::from_residual` has it
+#[lang = "from_yeet"]
+pub fn from_yeet<T, Y>(yeeted: Y) -> T
+where
+ T: FromResidual<Yeet<Y>>,
+{
+ FromResidual::from_residual(Yeet(yeeted))
+}
+
/// Allows retrieving the canonical type implementing [`Try`] that has this type
/// as its residual and allows it to hold an `O` as its output.
///
impl<T> Residual<T> for NeverShortCircuitResidual {
type TryType = NeverShortCircuit<T>;
}
+
+/// Implement `FromResidual<Yeet<T>>` on your type to enable
+/// `do yeet expr` syntax in functions returning your type.
+#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
+#[derive(Debug)]
+pub struct Yeet<T>(pub T);
}
}
+#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
+impl<T> ops::FromResidual<ops::Yeet<()>> for Option<T> {
+ #[inline]
+ fn from_residual(ops::Yeet(()): ops::Yeet<()>) -> Self {
+ None
+ }
+}
+
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
impl<T> ops::Residual<T> for Option<convert::Infallible> {
type TryType = Option<T>;
}
}
+#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
+impl<T, E, F: From<E>> ops::FromResidual<ops::Yeet<E>> for Result<T, F> {
+ #[inline]
+ fn from_residual(ops::Yeet(e): ops::Yeet<E>) -> Self {
+ Err(From::from(e))
+ }
+}
+
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
impl<T, E> ops::Residual<T> for Result<convert::Infallible, E> {
type TryType = Result<T, E>;
}
#[doc(hidden)]
+ #[inline]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
// SAFETY: the caller must guarantee that `i` is in bounds of
// the underlying slice, so `i` cannot overflow an `isize`, and
/// [`eprint!`] instead to print error and progress messages.
///
/// [flush]: crate::io::Write::flush
+/// [`println!`]: crate::println
+/// [`eprint!`]: crate::eprint
///
/// # Panics
///
/// [`eprintln!`] instead to print error and progress messages.
///
/// [`std::fmt`]: crate::fmt
+/// [`eprintln!`]: crate::eprintln
///
/// # Panics
///
///
/// [`io::stderr`]: crate::io::stderr
/// [`io::stdout`]: crate::io::stdout
+/// [`println!`]: crate::println
///
/// # Panics
///
/// The [`with`] method yields a reference to the contained value which cannot be
/// sent across threads or escape the given closure.
///
+/// [`thread_local!`]: crate::thread_local
+///
/// # Initialization and Destruction
///
/// Initialization is dynamically performed on the first call to [`with`]
//! [`Cell`]: crate::cell::Cell
//! [`RefCell`]: crate::cell::RefCell
//! [`with`]: LocalKey::with
+//! [`thread_local!`]: crate::thread_local
#![stable(feature = "rust1", since = "1.0.0")]
#![deny(unsafe_op_in_unsafe_fn)]
dist::RustcDocs,
dist::Mingw,
dist::Rustc,
- dist::DebuggerScripts,
dist::Std,
dist::RustcDev,
dist::Analysis,
// FIXME(davidtwco): #[cfg(not(bootstrap))] - #95612 needs to be in the bootstrap compiler
// for this conditional to be removed.
if !target.contains("windows") || compiler.stage >= 1 {
- if target.contains("linux") || target.contains("windows") {
+ if target.contains("linux") || target.contains("windows") || target.contains("openbsd")
+ {
rustflags.arg("-Zunstable-options");
}
match self.config.rust_split_debuginfo {
type Output = ();
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.path("src/etc/lldb_batchmode.py")
- }
-
- fn make_run(run: RunConfig<'_>) {
- run.builder.ensure(DebuggerScripts {
- sysroot: run
- .builder
- .sysroot(run.builder.compiler(run.builder.top_stage, run.build_triple())),
- host: run.target,
- });
+ run.never()
}
/// Copies debugger scripts for `target` into the `sysroot` specified.
.env("RUSTFLAGS", "-Cdebuginfo=2")
.env("CARGO_TARGET_DIR", builder.out.join("bootstrap"))
.env("RUSTC_BOOTSTRAP", "1")
+ .env("RUSTDOC", builder.rustdoc(builder.compiler(0, builder.build.build)))
.env("RUSTC", &builder.initial_rustc);
if let Some(flags) = option_env!("RUSTFLAGS") {
// Use the same rustc flags for testing as for "normal" compilation,
if !builder.fail_fast {
cmd.arg("--no-fail-fast");
}
+ match builder.doc_tests {
+ DocTests::Only => {
+ cmd.arg("--doc");
+ }
+ DocTests::No => {
+ cmd.args(&["--lib", "--bins", "--examples", "--tests", "--benches"]);
+ }
+ DocTests::Yes => {}
+ }
+
cmd.arg("--").args(&builder.config.cmd.test_args());
// rustbuild tests are racy on directory creation so just run them one at a time.
// Since there's not many this shouldn't be a problem.
-0.8.5
\ No newline at end of file
+0.9.0
\ No newline at end of file
The modifier does nothing for linkers that don't support it.
The default for this modifier is `-whole-archive`. \
-NOTE: The default may currently be different when building dylibs for some targets,
-but it is not guaranteed.
+NOTE: The default may currently be different in some cases for backward compatibility,
+but it is not guaranteed. If you need whole archive semantics use `+whole-archive` explicitly.
<a id="option-crate-type"></a>
## `--crate-type`: a list of types of crates for the compiler to emit
'force-warn' does for 'warn'. It's the same as 'deny' in that a lint at this
level will produce an error, but unlike the 'deny' level, the 'forbid' level
can not be overridden to be anything lower than an error. However, lint
-levels may still be capped with `--cap-lints` (see below) so `rustc --cap-
-lints warn` will make lints set to 'forbid' just
-warn.
+levels may still be capped with `--cap-lints` (see below) so `rustc --cap-lints warn`
+will make lints set to 'forbid' just warn.
## Configuring warning levels
--- /dev/null
+# `yeet_expr`
+
+The tracking issue for this feature is: [#96373]
+
+[#96373]: https://github.com/rust-lang/rust/issues/96373
+
+------------------------
+
+The `yeet_expr` feature adds support for `do yeet` expressions,
+which can be used to early-exit from a function or `try` block.
+
+These are highly experimental, thus the placeholder syntax.
+
+```rust,edition2021
+#![feature(yeet_expr)]
+
+fn foo() -> Result<String, i32> {
+ do yeet 4;
+}
+assert_eq!(foo(), Err(4));
+
+fn bar() -> Option<String> {
+ do yeet;
+}
+assert_eq!(bar(), None);
+```
// `self_def_id` set, we override it here.
// See https://github.com/rust-lang/rust/issues/85454
if let QPath { ref mut self_def_id, .. } = default {
- *self_def_id = cx.tcx.parent(self.def_id);
+ *self_def_id = Some(cx.tcx.parent(self.def_id));
}
Some(default)
let mut what_rustc_thinks =
Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx);
- let impl_ref = cx.tcx.parent(local_did).and_then(|did| cx.tcx.impl_trait_ref(did));
+ let impl_ref = cx.tcx.impl_trait_ref(cx.tcx.local_parent(self.def_id));
// Trait impl items always inherit the impl's visibility --
// we don't want to show `pub`.
let mut what_rustc_thinks =
Item::from_def_id_and_parts(self.def_id, Some(self.name), kind, cx);
- let impl_ref = tcx.parent(self.def_id).and_then(|did| tcx.impl_trait_ref(did));
+ let impl_ref = tcx.impl_trait_ref(tcx.parent(self.def_id));
// Trait impl items always inherit the impl's visibility --
// we don't want to show `pub`.
}
fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- let parent = tcx
- .parent(def_id)
- .expect("is_field_vis_inherited can only be called on struct or variant fields");
+ let parent = tcx.parent(def_id);
match tcx.def_kind(parent) {
DefKind::Struct | DefKind::Union => false,
DefKind::Variant => true,
let mut current = def_id;
// The immediate parent might not always be a module.
// Find the first parent which is.
- while let Some(parent) = tcx.parent(current) {
+ while let Some(parent) = tcx.opt_parent(current) {
if tcx.def_kind(parent) == DefKind::Mod {
return Some(parent);
}
let did = match def_kind {
DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant => {
// documented on their parent's page
- tcx.parent(did).unwrap()
+ tcx.parent(did)
}
_ => did,
};
let mut map = FxHashMap::default();
// This is the list of IDs used in Javascript.
map.insert("help".into(), 1);
+ map.insert("settings".into(), 1);
+ map.insert("not-displayed".into(), 1);
+ map.insert("alternative-display".into(), 1);
+ map.insert("search".into(), 1);
// This is the list of IDs used in HTML generated in Rust (including the ones
// used in tera template files).
map.insert("mainThemeStyle".into(), 1);
map.insert("settings-menu".into(), 1);
map.insert("help-button".into(), 1);
map.insert("main-content".into(), 1);
- map.insert("search".into(), 1);
map.insert("crate-search".into(), 1);
map.insert("render-detail".into(), 1);
map.insert("toggle-all-docs".into(), 1);
use super::search_index::build_index;
use super::write_shared::write_shared;
use super::{
- collect_spans_and_sources, print_sidebar, scrape_examples_help, settings, AllTypes,
- LinkFromSrc, NameDoc, StylePath, BASIC_KEYWORDS,
+ collect_spans_and_sources, print_sidebar, scrape_examples_help, AllTypes, LinkFromSrc, NameDoc,
+ StylePath, BASIC_KEYWORDS,
};
use crate::clean::{self, types::ExternalLocation, ExternalCrate};
page.root_path = "./";
let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>";
- let theme_names: Vec<String> = self
- .shared
- .style_files
- .iter()
- .map(StylePath::basename)
- .collect::<Result<_, Error>>()?;
let v = layout::render(
&self.shared.layout,
&page,
sidebar,
- settings(
- self.shared.static_root_path.as_deref().unwrap_or("./"),
- &self.shared.resource_suffix,
- theme_names,
- )?,
+ |buf: &mut Buffer| {
+ write!(
+ buf,
+ "<script defer src=\"{}settings{}.js\"></script>",
+ page.static_root_path.unwrap_or(""),
+ page.resource_suffix
+ )
+ },
&self.shared.style_files,
);
self.shared.fs.write(settings_file, v)?;
}
}
-#[derive(Debug)]
-enum Setting {
- Section {
- description: &'static str,
- sub_settings: Vec<Setting>,
- },
- Toggle {
- js_data_name: &'static str,
- description: &'static str,
- default_value: bool,
- },
- Select {
- js_data_name: &'static str,
- description: &'static str,
- default_value: &'static str,
- options: Vec<String>,
- },
-}
-
-impl Setting {
- fn display(&self, root_path: &str, suffix: &str) -> String {
- match *self {
- Setting::Section { description, ref sub_settings } => format!(
- "<div class=\"setting-line\">\
- <div class=\"title\">{}</div>\
- <div class=\"sub-settings\">{}</div>
- </div>",
- description,
- sub_settings.iter().map(|s| s.display(root_path, suffix)).collect::<String>()
- ),
- Setting::Toggle { js_data_name, description, default_value } => format!(
- "<div class=\"setting-line\">\
- <label class=\"toggle\">\
- <input type=\"checkbox\" id=\"{}\" {}>\
- <span class=\"slider\"></span>\
- </label>\
- <div>{}</div>\
- </div>",
- js_data_name,
- if default_value { " checked" } else { "" },
- description,
- ),
- Setting::Select { js_data_name, description, default_value, ref options } => format!(
- "<div class=\"setting-line\"><div class=\"radio-line\" id=\"{}\"><span class=\"setting-name\">{}</span><div class=\"choices\">{}</div></div></div>",
- js_data_name,
- description,
- options
- .iter()
- .map(|opt| format!(
- "<label for=\"{js_data_name}-{name}\" class=\"choice\">
- <input type=\"radio\" name=\"{js_data_name}\" id=\"{js_data_name}-{name}\" value=\"{name}\" {checked}>\
- {name}\
- </label>",
- js_data_name = js_data_name,
- name = opt,
- checked = if opt == default_value { "checked" } else { "" },
- ))
- .collect::<String>(),
- ),
- }
- }
-}
-
-impl From<(&'static str, &'static str, bool)> for Setting {
- fn from(values: (&'static str, &'static str, bool)) -> Setting {
- Setting::Toggle { js_data_name: values.0, description: values.1, default_value: values.2 }
- }
-}
-
-impl<T: Into<Setting>> From<(&'static str, Vec<T>)> for Setting {
- fn from(values: (&'static str, Vec<T>)) -> Setting {
- Setting::Section {
- description: values.0,
- sub_settings: values.1.into_iter().map(|v| v.into()).collect::<Vec<_>>(),
- }
- }
-}
-
-fn settings(root_path: &str, suffix: &str, theme_names: Vec<String>) -> Result<String, Error> {
- // (id, explanation, default value)
- let settings: &[Setting] = &[
- Setting::from(("use-system-theme", "Use system theme", true)),
- Setting::Select {
- js_data_name: "theme",
- description: "Theme",
- default_value: "light",
- options: theme_names.clone(),
- },
- Setting::Select {
- js_data_name: "preferred-light-theme",
- description: "Preferred light theme",
- default_value: "light",
- options: theme_names.clone(),
- },
- Setting::Select {
- js_data_name: "preferred-dark-theme",
- description: "Preferred dark theme",
- default_value: "dark",
- options: theme_names,
- },
- ("auto-hide-large-items", "Auto-hide item contents for large items.", true).into(),
- ("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(),
- ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", false)
- .into(),
- ("go-to-only-result", "Directly go to item in search if there is only one result", false)
- .into(),
- ("line-numbers", "Show line numbers on code examples", false).into(),
- ("disable-shortcuts", "Disable keyboard shortcuts", false).into(),
- ];
-
- Ok(format!(
- "<div class=\"main-heading\">
- <h1 class=\"fqn\">\
- <span class=\"in-band\">Rustdoc settings</span>\
- </h1>\
- <span class=\"out-of-band\">\
- <a id=\"back\" href=\"javascript:void(0)\">Back</a>\
- </span>\
- </div>\
- <div class=\"settings\">{}</div>\
- <link rel=\"stylesheet\" href=\"{root_path}settings{suffix}.css\">\
- <script src=\"{root_path}settings{suffix}.js\"></script>",
- settings.iter().map(|s| s.display(root_path, suffix)).collect::<String>(),
- root_path = root_path,
- suffix = suffix
- ))
-}
-
fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned();
content.push_str(&format!(
return getVar("root-path") + basename + getVar("resource-suffix") + extension;
}
+function hideMain() {
+ addClass(document.getElementById(MAIN_ID), "hidden");
+}
+
+function showMain() {
+ removeClass(document.getElementById(MAIN_ID), "hidden");
+}
+
(function () {
window.rootPath = getVar("root-path");
window.currentCrate = getVar("current-crate");
window.searchJS = resourcePath("search", ".js");
window.searchIndexJS = resourcePath("search-index", ".js");
+ window.settingsJS = resourcePath("settings", ".js");
const sidebarVars = document.getElementById("sidebar-vars");
if (sidebarVars) {
window.sidebarCurrent = {
const THEME_PICKER_ELEMENT_ID = "theme-picker";
const THEMES_ELEMENT_ID = "theme-choices";
const MAIN_ID = "main-content";
+const SETTINGS_BUTTON_ID = "settings-menu";
+const ALTERNATIVE_DISPLAY_ID = "alternative-display";
+const NOT_DISPLAYED_ID = "not-displayed";
function getThemesElement() {
return document.getElementById(THEMES_ELEMENT_ID);
return document.getElementById(THEME_PICKER_ELEMENT_ID);
}
+function getSettingsButton() {
+ return document.getElementById(SETTINGS_BUTTON_ID);
+}
+
// Returns the current URL without any query parameter or hash.
function getNakedUrl() {
return window.location.href.split("?")[0].split("#")[0];
themePicker.style.borderBottomLeftRadius = "3px";
}
+window.hideSettings = function() {
+ // Does nothing by default.
+};
+
// Set up the theme picker list.
(function () {
if (!document.location.href.startsWith("file:///")) {
});
}());
+/**
+ * This function inserts `newNode` after `referenceNode`. It doesn't work if `referenceNode`
+ * doesn't have a parent node.
+ *
+ * @param {HTMLElement} newNode
+ * @param {HTMLElement} referenceNode
+ */
+function insertAfter(newNode, referenceNode) {
+ referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
+}
+
+/**
+ * This function creates a new `<section>` with the given `id` and `classes` if it doesn't already
+ * exist.
+ *
+ * More information about this in `switchDisplayedElement` documentation.
+ *
+ * @param {string} id
+ * @param {string} classes
+ */
+function getOrCreateSection(id, classes) {
+ let el = document.getElementById(id);
+
+ if (!el) {
+ el = document.createElement("section");
+ el.id = id;
+ el.className = classes;
+ insertAfter(el, document.getElementById(MAIN_ID));
+ }
+ return el;
+}
+
+/**
+ * Returns the `<section>` element which contains the displayed element.
+ *
+ * @return {HTMLElement}
+ */
+function getAlternativeDisplayElem() {
+ return getOrCreateSection(ALTERNATIVE_DISPLAY_ID, "content hidden");
+}
+
+/**
+ * Returns the `<section>` element which contains the not-displayed elements.
+ *
+ * @return {HTMLElement}
+ */
+function getNotDisplayedElem() {
+ return getOrCreateSection(NOT_DISPLAYED_ID, "hidden");
+}
+
+/**
+ * To nicely switch between displayed "extra" elements (such as search results or settings menu)
+ * and to alternate between the displayed and not displayed elements, we hold them in two different
+ * `<section>` elements. They work in pair: one holds the hidden elements while the other
+ * contains the displayed element (there can be only one at the same time!). So basically, we switch
+ * elements between the two `<section>` elements.
+ *
+ * @param {HTMLElement} elemToDisplay
+ */
+function switchDisplayedElement(elemToDisplay) {
+ const el = getAlternativeDisplayElem();
+
+ if (el.children.length > 0) {
+ getNotDisplayedElem().appendChild(el.firstElementChild);
+ }
+ if (elemToDisplay === null) {
+ addClass(el, "hidden");
+ showMain();
+ return;
+ }
+ el.appendChild(elemToDisplay);
+ hideMain();
+ removeClass(el, "hidden");
+}
+
+function browserSupportsHistoryApi() {
+ return window.history && typeof window.history.pushState === "function";
+}
+
+// eslint-disable-next-line no-unused-vars
+function loadCss(cssFileName) {
+ const link = document.createElement("link");
+ link.href = resourcePath(cssFileName, ".css");
+ link.type = "text/css";
+ link.rel = "stylesheet";
+ document.getElementsByTagName("head")[0].appendChild(link);
+}
+
(function() {
"use strict";
+ function loadScript(url) {
+ const script = document.createElement('script');
+ script.src = url;
+ document.head.append(script);
+ }
+
+
+ getSettingsButton().onclick = function(event) {
+ event.preventDefault();
+ loadScript(window.settingsJS);
+ };
+
window.searchState = {
loadingText: "Loading search results...",
input: document.getElementsByClassName("search-input")[0],
outputElement: function() {
- return document.getElementById("search");
+ let el = document.getElementById("search");
+ if (!el) {
+ el = document.createElement("section");
+ el.id = "search";
+ getNotDisplayedElem().appendChild(el);
+ }
+ return el;
},
title: document.title,
titleBeforeSearch: document.title,
searchState.timeout = null;
}
},
+ isDisplayed: function() {
+ return searchState.outputElement().parentElement.id === ALTERNATIVE_DISPLAY_ID;
+ },
// Sets the focus on the search bar at the top of the page
focus: function() {
searchState.input.focus();
if (search === null || typeof search === 'undefined') {
search = searchState.outputElement();
}
- addClass(main, "hidden");
- removeClass(search, "hidden");
+ switchDisplayedElement(search);
searchState.mouseMovedAfterSearch = false;
document.title = searchState.title;
},
- hideResults: function(search) {
- if (search === null || typeof search === 'undefined') {
- search = searchState.outputElement();
- }
- addClass(search, "hidden");
- removeClass(main, "hidden");
+ hideResults: function() {
+ switchDisplayedElement(null);
document.title = searchState.titleBeforeSearch;
// We also remove the query parameter from the URL.
- if (searchState.browserSupportsHistoryApi()) {
+ if (browserSupportsHistoryApi()) {
history.replaceState(null, window.currentCrate + " - Rust",
getNakedUrl() + window.location.hash);
}
});
return params;
},
- browserSupportsHistoryApi: function() {
- return window.history && typeof window.history.pushState === "function";
- },
setup: function() {
const search_input = searchState.input;
if (!searchState.input) {
return;
}
- function loadScript(url) {
- const script = document.createElement('script');
- script.src = url;
- document.head.append(script);
- }
-
let searchLoaded = false;
function loadSearch() {
if (!searchLoaded) {
}
const toggleAllDocsId = "toggle-all-docs";
- const main = document.getElementById(MAIN_ID);
let savedHash = "";
function handleHashes(ev) {
- let elem;
- const search = searchState.outputElement();
- if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
+ if (ev !== null && searchState.isDisplayed() && ev.newURL) {
// This block occurs when clicking on an element in the navbar while
// in a search.
- searchState.hideResults(search);
+ switchDisplayedElement(null);
const hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
- if (searchState.browserSupportsHistoryApi()) {
+ if (browserSupportsHistoryApi()) {
// `window.location.search`` contains all the query parameters, not just `search`.
history.replaceState(null, "",
getNakedUrl() + window.location.search + "#" + hash);
}
- elem = document.getElementById(hash);
+ const elem = document.getElementById(hash);
if (elem) {
elem.scrollIntoView();
}
}
function handleEscape(ev) {
+ searchState.clearInputTimeout();
const help = getHelpElement(false);
- const search = searchState.outputElement();
if (help && !hasClass(help, "hidden")) {
displayHelp(false, ev, help);
- } else if (search && !hasClass(search, "hidden")) {
- searchState.clearInputTimeout();
+ } else {
+ switchDisplayedElement(null);
+ if (browserSupportsHistoryApi()) {
+ history.replaceState(null, window.currentCrate + " - Rust",
+ getNakedUrl() + window.location.hash);
+ }
ev.preventDefault();
- searchState.hideResults(search);
}
searchState.defocus();
hideThemeButtonState();
innerToggle.children[0].innerText = labelForToggleButton(sectionIsCollapsed);
}
- function insertAfter(newNode, referenceNode) {
- referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
- }
-
(function() {
const toggles = document.getElementById(toggleAllDocsId);
if (toggles) {
/* eslint no-var: "error" */
/* eslint prefer-const: "error" */
/* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */
-/* global onEachLazy, removeClass, searchState, hasClass */
+/* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi */
(function() {
// This mapping table should match the discriminants of
// Because searching is incremental by character, only the most
// recent search query is added to the browser history.
- if (searchState.browserSupportsHistoryApi()) {
+ if (browserSupportsHistoryApi()) {
const newURL = buildUrl(query.original, filterCrates);
+
if (!history.state && !params.search) {
history.pushState(null, "", newURL);
} else {
if (!searchState.input) {
return;
}
- const search = searchState.outputElement();
- if (search_input.value !== "" && hasClass(search, "hidden")) {
- searchState.showResults(search);
- if (searchState.browserSupportsHistoryApi()) {
+ if (search_input.value !== "" && !searchState.isDisplayed()) {
+ searchState.showResults();
+ if (browserSupportsHistoryApi()) {
history.replaceState(null, "",
buildUrl(search_input.value, getFilterCrates()));
}
const searchAfter500ms = function() {
searchState.clearInputTimeout();
if (searchState.input.value.length === 0) {
- if (searchState.browserSupportsHistoryApi()) {
+ if (browserSupportsHistoryApi()) {
history.replaceState(null, window.currentCrate + " - Rust",
getNakedUrl() + window.location.hash);
}
// Push and pop states are used to add search results to the browser
// history.
- if (searchState.browserSupportsHistoryApi()) {
+ if (browserSupportsHistoryApi()) {
// Store the previous <title> so we can revert back to it later.
const previousTitle = document.title;
/* eslint no-var: "error" */
/* eslint prefer-const: "error" */
// Local js definitions:
-/* global getSettingValue, getVirtualKey, onEachLazy, updateLocalStorage, updateSystemTheme */
-/* global addClass, removeClass */
+/* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme, loadCss */
+/* global addClass, removeClass, onEach, onEachLazy, NOT_DISPLAYED_ID */
+/* global MAIN_ID, getVar, getSettingsButton, switchDisplayedElement, getNotDisplayedElem */
(function () {
+ const isSettingsPage = window.location.pathname.endsWith("/settings.html");
+
function changeSetting(settingName, value) {
updateLocalStorage(settingName, value);
}
}
- function setEvents() {
+ function setEvents(settingsElement) {
updateLightAndDark();
- onEachLazy(document.getElementsByClassName("slider"), function(elem) {
+ onEachLazy(settingsElement.getElementsByClassName("slider"), function(elem) {
const toggle = elem.previousElementSibling;
const settingId = toggle.id;
const settingValue = getSettingValue(settingId);
toggle.onkeyup = handleKey;
toggle.onkeyrelease = handleKey;
});
- onEachLazy(document.getElementsByClassName("select-wrapper"), function(elem) {
+ onEachLazy(settingsElement.getElementsByClassName("select-wrapper"), function(elem) {
const select = elem.getElementsByTagName("select")[0];
const settingId = select.id;
const settingValue = getSettingValue(settingId);
changeSetting(this.id, this.value);
};
});
- onEachLazy(document.querySelectorAll("input[type=\"radio\"]"), function(elem) {
+ onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), function(elem) {
const settingId = elem.name;
const settingValue = getSettingValue(settingId);
if (settingValue !== null && settingValue !== "null") {
changeSetting(ev.target.name, ev.target.value);
});
});
- document.getElementById("back").addEventListener("click", function() {
- history.back();
- });
}
- window.addEventListener("DOMContentLoaded", setEvents);
+ /**
+ * This function builds the sections inside the "settings page". It takes a `settings` list
+ * as argument which describes each setting and how to render it. It returns a string
+ * representing the raw HTML.
+ *
+ * @param {Array<Object>} settings
+ *
+ * @return {string}
+ */
+ function buildSettingsPageSections(settings) {
+ let output = "";
+
+ for (const setting of settings) {
+ output += `<div class="setting-line">`;
+ const js_data_name = setting["js_name"];
+ const setting_name = setting["name"];
+
+ if (setting["options"] !== undefined) {
+ // This is a select setting.
+ output += `<div class="radio-line" id="${js_data_name}">\
+ <span class="setting-name">${setting_name}</span>\
+ <div class="choices">`;
+ onEach(setting["options"], function(option) {
+ const checked = option === setting["default"] ? " checked" : "";
+
+ output += `<label for="${js_data_name}-${option}" class="choice">\
+ <input type="radio" name="${js_data_name}" \
+ id="${js_data_name}-${option}" value="${option}"${checked}>\
+ ${option}\
+ </label>`;
+ });
+ output += "</div></div>";
+ } else {
+ // This is a toggle.
+ const checked = setting["default"] === true ? " checked" : "";
+ output += `
+ <label class="toggle">
+ <input type="checkbox" id="${js_data_name}"${checked}>
+ <span class="slider"></span>
+ </label>
+ <div>${setting_name}</div>`;
+ }
+ output += "</div>";
+ }
+ return output;
+ }
+
+ /**
+ * This function builds the "settings page" and returns the generated HTML element.
+ *
+ * @return {HTMLElement}
+ */
+ function buildSettingsPage() {
+ const themes = getVar("themes").split(",");
+ const settings = [
+ {
+ "name": "Use system theme",
+ "js_name": "use-system-theme",
+ "default": true,
+ },
+ {
+ "name": "Theme",
+ "js_name": "theme",
+ "default": "light",
+ "options": themes,
+ },
+ {
+ "name": "Preferred light theme",
+ "js_name": "preferred-light-theme",
+ "default": "light",
+ "options": themes,
+ },
+ {
+ "name": "Preferred dark theme",
+ "js_name": "preferred-dark-theme",
+ "default": "dark",
+ "options": themes,
+ },
+ {
+ "name": "Auto-hide item contents for large items",
+ "js_name": "auto-hide-large-items",
+ "default": true,
+ },
+ {
+ "name": "Auto-hide item methods' documentation",
+ "js_name": "auto-hide-method-docs",
+ "default": false,
+ },
+ {
+ "name": "Auto-hide trait implementation documentation",
+ "js_name": "auto-hide-trait-implementations",
+ "default": false,
+ },
+ {
+ "name": "Directly go to item in search if there is only one result",
+ "js_name": "go-to-only-result",
+ "default": false,
+ },
+ {
+ "name": "Show line numbers on code examples",
+ "js_name": "line-numbers",
+ "default": false,
+ },
+ {
+ "name": "Disable keyboard shortcuts",
+ "js_name": "disable-shortcuts",
+ "default": false,
+ },
+ ];
+
+ // First, we add the settings.css file.
+ loadCss("settings");
+
+ // Then we build the DOM.
+ const el = document.createElement("section");
+ el.id = "settings";
+ let innerHTML = `
+ <div class="main-heading">
+ <h1 class="fqn">
+ <span class="in-band">Rustdoc settings</span>
+ </h1>
+ <span class="out-of-band">`;
+
+ if (isSettingsPage) {
+ innerHTML +=
+ `<a id="back" href="javascript:void(0)" onclick="history.back();">Back</a>`;
+ } else {
+ innerHTML +=
+ `<a id="back" href="javascript:void(0)" onclick="switchDisplayedElement(null);">\
+ Back</a>`;
+ }
+ innerHTML += `</span>
+ </div>
+ <div class="settings">${buildSettingsPageSections(settings)}</div>`;
+
+ el.innerHTML = innerHTML;
+
+ if (isSettingsPage) {
+ document.getElementById(MAIN_ID).appendChild(el);
+ } else {
+ getNotDisplayedElem().appendChild(el);
+ }
+ return el;
+ }
+
+ const settingsMenu = buildSettingsPage();
+
+ if (isSettingsPage) {
+ // We replace the existing "onclick" callback to do nothing if clicked.
+ getSettingsButton().onclick = function(event) {
+ event.preventDefault();
+ };
+ } else {
+ // We replace the existing "onclick" callback.
+ const settingsButton = getSettingsButton();
+ settingsButton.onclick = function(event) {
+ event.preventDefault();
+ if (settingsMenu.parentElement.id === NOT_DISPLAYED_ID) {
+ switchDisplayedElement(settingsMenu);
+ } else {
+ window.hideSettings();
+ }
+ };
+ window.hideSettings = function() {
+ switchDisplayedElement(null);
+ };
+ }
+
+ // We now wait a bit for the web browser to end re-computing the DOM...
+ setTimeout(function() {
+ setEvents(settingsMenu);
+ // The setting menu is already displayed if we're on the settings page.
+ if (!isSettingsPage) {
+ switchDisplayedElement(settingsMenu);
+ }
+ }, 0);
})();
/* global search, sourcesIndex */
// Local js definitions:
-/* global addClass, getCurrentValue, hasClass, onEachLazy, removeClass, searchState */
+/* global addClass, getCurrentValue, hasClass, onEachLazy, removeClass, browserSupportsHistoryApi */
/* global updateLocalStorage */
(function() {
const set_fragment = function(name) {
const x = window.scrollX,
y = window.scrollY;
- if (searchState.browserSupportsHistoryApi()) {
+ if (browserSupportsHistoryApi()) {
history.replaceState(null, null, "#" + name);
highlightSourceLines();
} else {
</nav> {#- -#}
</div> {#- -#}
<section id="main-content" class="content">{{- content|safe -}}</section> {#- -#}
- <section id="search" class="content hidden"></section> {#- -#}
</div> {#- -#}
</main> {#- -#}
{{- layout.external_html.after_content|safe -}}
let should_be_ignored = i
.item_id
.as_def_id()
- .and_then(|def_id| self.ctx.tcx.parent(def_id))
+ .and_then(|def_id| self.ctx.tcx.opt_parent(def_id))
.and_then(|def_id| self.ctx.tcx.hir().get_if_local(def_id))
.map(|node| {
matches!(
use rustc_hir::Mutability;
use rustc_middle::ty::{DefIdTree, Ty, TyCtxt};
use rustc_middle::{bug, span_bug, ty};
+use rustc_resolve::ParentScope;
use rustc_session::lint::Lint;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{sym, Ident, Symbol};
FragmentKind::StructField => write!(s, "structfield.{}", name),
FragmentKind::Variant => write!(s, "variant.{}", name),
FragmentKind::VariantField => {
- let variant = tcx.item_name(tcx.parent(def_id).unwrap());
+ let variant = tcx.item_name(tcx.parent(def_id));
write!(s, "variant.{}.field.{}", variant, name)
}
}
| DefKind::AssocTy
| DefKind::Variant
| DefKind::Field) => {
- let parent_def_id = tcx.parent(def_id).expect("nested item has no parent");
+ let parent_def_id = tcx.parent(def_id);
if def_kind == DefKind::Field && tcx.def_kind(parent_def_id) == DefKind::Variant
{
- tcx.parent(parent_def_id).expect("variant has no parent")
+ tcx.parent(parent_def_id)
} else {
parent_def_id
}
.copied()
.unwrap_or_else(|| {
self.cx.enter_resolver(|resolver| {
- resolver.resolve_rustdoc_path(path_str, ns, module_id)
+ let parent_scope =
+ ParentScope::module(resolver.expect_module(module_id), resolver);
+ resolver.resolve_rustdoc_path(path_str, ns, parent_scope)
})
})
.and_then(|res| res.try_into().ok())
cx: &DocContext<'_>,
res: Res,
) -> Result<(Res, Option<ItemFragment>), ErrorKind<'static>> {
- cx.tcx
- .parent(res.def_id(cx.tcx))
- .map(|parent| {
- let parent_def = Res::Def(DefKind::Enum, parent);
- let variant = cx.tcx.expect_variant_res(res.as_hir_res().unwrap());
- (parent_def, Some(ItemFragment(FragmentKind::Variant, variant.def_id)))
- })
- .ok_or_else(|| ResolutionFailure::NoParentItem.into())
+ let parent = cx.tcx.parent(res.def_id(cx.tcx));
+ let parent_def = Res::Def(DefKind::Enum, parent);
+ let variant = cx.tcx.expect_variant_res(res.as_hir_res().unwrap());
+ Ok((parent_def, Some(ItemFragment(FragmentKind::Variant, variant.def_id))))
}
/// Resolve a primitive type or value.
use crate::clean::Attributes;
use crate::core::ResolverCaches;
use crate::passes::collect_intra_doc_links::preprocessed_markdown_links;
-use crate::passes::collect_intra_doc_links::PreprocessedMarkdownLink;
+use crate::passes::collect_intra_doc_links::{Disambiguator, PreprocessedMarkdownLink};
use rustc_ast::visit::{self, AssocCtxt, Visitor};
use rustc_ast::{self as ast, ItemKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::Namespace::*;
use rustc_hir::def::{DefKind, Namespace, Res};
-use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, CRATE_DEF_ID};
+use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, CRATE_DEF_ID};
use rustc_hir::TraitCandidate;
use rustc_middle::ty::{DefIdTree, Visibility};
use rustc_resolve::{ParentScope, Resolver};
use rustc_session::config::Externs;
use rustc_session::Session;
+use rustc_span::symbol::sym;
use rustc_span::{Symbol, SyntaxContext};
use std::collections::hash_map::Entry;
externs: Externs,
document_private_items: bool,
) -> ResolverCaches {
+ let parent_scope =
+ ParentScope::module(resolver.expect_module(CRATE_DEF_ID.to_def_id()), resolver);
let mut link_resolver = EarlyDocLinkResolver {
resolver,
sess,
- current_mod: CRATE_DEF_ID,
+ parent_scope,
visited_mods: Default::default(),
markdown_links: Default::default(),
doc_link_resolutions: Default::default(),
// DO NOT REMOVE THIS without first testing on the reproducer in
// https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) {
- link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, CRATE_DEF_ID.to_def_id());
+ link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope);
}
ResolverCaches {
struct EarlyDocLinkResolver<'r, 'ra> {
resolver: &'r mut Resolver<'ra>,
sess: &'r Session,
- current_mod: LocalDefId,
+ parent_scope: ParentScope<'ra>,
visited_mods: DefIdSet,
markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>,
doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<ast::NodeId>>>,
document_private_items: bool,
}
-impl EarlyDocLinkResolver<'_, '_> {
+impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
fn add_traits_in_scope(&mut self, def_id: DefId) {
// Calls to `traits_in_scope` are expensive, so try to avoid them if only possible.
// Keys in the `traits_in_scope` cache are always module IDs.
return;
}
// FIXME: actually resolve links, not just add traits in scope.
- if let Some(parent_id) = self.resolver.parent(scope_id) {
+ if let Some(parent_id) = self.resolver.opt_parent(scope_id) {
self.add_traits_in_scope(parent_id);
}
}
if !attrs.iter().any(|attr| attr.may_have_doc_links()) {
return;
}
- let module_id = self.current_mod.to_def_id();
- self.resolve_doc_links(doc_attrs(attrs.iter()), module_id);
+ self.resolve_doc_links(doc_attrs(attrs.iter()), self.parent_scope);
}
- fn resolve_doc_links(&mut self, attrs: Attributes, module_id: DefId) {
+ fn resolve_and_cache(
+ &mut self,
+ path_str: &str,
+ ns: Namespace,
+ parent_scope: &ParentScope<'ra>,
+ ) -> bool {
+ // FIXME: This caching may be incorrect in case of multiple `macro_rules`
+ // items with the same name in the same module.
+ self.doc_link_resolutions
+ .entry((Symbol::intern(path_str), ns, parent_scope.module.def_id()))
+ .or_insert_with_key(|(path, ns, _)| {
+ self.resolver.resolve_rustdoc_path(path.as_str(), *ns, *parent_scope)
+ })
+ .is_some()
+ }
+
+ fn resolve_doc_links(&mut self, attrs: Attributes, parent_scope: ParentScope<'ra>) {
let mut need_traits_in_scope = false;
for (doc_module, doc) in attrs.prepare_to_doc_link_resolution() {
assert_eq!(doc_module, None);
- let links = self
- .markdown_links
- .entry(doc)
- .or_insert_with_key(|doc| preprocessed_markdown_links(doc));
+ let mut tmp_links = mem::take(&mut self.markdown_links);
+ let links =
+ tmp_links.entry(doc).or_insert_with_key(|doc| preprocessed_markdown_links(doc));
for PreprocessedMarkdownLink(pp_link, _) in links {
if let Ok(pinfo) = pp_link {
- // FIXME: Resolve the path in all namespaces and resolve its prefixes too.
- let ns = TypeNS;
- self.doc_link_resolutions
- .entry((Symbol::intern(&pinfo.path_str), ns, module_id))
- .or_insert_with_key(|(path, ns, module_id)| {
- self.resolver.resolve_rustdoc_path(path.as_str(), *ns, *module_id)
- });
- need_traits_in_scope = true;
+ // The logic here is a conservative approximation for path resolution in
+ // `resolve_with_disambiguator`.
+ if let Some(ns) = pinfo.disambiguator.map(Disambiguator::ns) {
+ if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
+ continue;
+ }
+ }
+
+ // Resolve all namespaces due to no disambiguator or for diagnostics.
+ let mut any_resolved = false;
+ let mut need_assoc = false;
+ for ns in [TypeNS, ValueNS, MacroNS] {
+ if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
+ any_resolved = true;
+ } else if ns != MacroNS {
+ need_assoc = true;
+ }
+ }
+
+ // FIXME: Resolve all prefixes for type-relative resolution or for diagnostics.
+ if (need_assoc || !any_resolved) && pinfo.path_str.contains("::") {
+ need_traits_in_scope = true;
+ }
}
}
+ self.markdown_links = tmp_links;
}
if need_traits_in_scope {
- self.add_traits_in_scope(module_id);
+ self.add_traits_in_scope(parent_scope.module.def_id());
}
}
{
if let Some(def_id) = child.res.opt_def_id() && !def_id.is_local() {
let scope_id = match child.res {
- Res::Def(DefKind::Variant, ..) => self.resolver.parent(def_id).unwrap(),
+ Res::Def(DefKind::Variant, ..) => self.resolver.parent(def_id),
_ => def_id,
};
self.resolve_doc_links_extern_outer(def_id, scope_id); // Outer attribute scope
if let Res::Def(DefKind::Mod, ..) = child.res {
self.resolve_doc_links_extern_inner(def_id); // Inner attribute scope
}
- // Traits are processed in `add_extern_traits_in_scope`.
+ // `DefKind::Trait`s are processed in `process_extern_impls`.
if let Res::Def(DefKind::Mod | DefKind::Enum, ..) = child.res {
self.process_module_children_or_reexports(def_id);
}
+ if let Res::Def(DefKind::Struct | DefKind::Union | DefKind::Variant, _) =
+ child.res
+ {
+ let field_def_ids = Vec::from_iter(
+ self.resolver
+ .cstore()
+ .associated_item_def_ids_untracked(def_id, self.sess),
+ );
+ for field_def_id in field_def_ids {
+ self.resolve_doc_links_extern_outer(field_def_id, scope_id);
+ }
+ }
}
}
}
fn visit_item(&mut self, item: &ast::Item) {
self.resolve_doc_links_local(&item.attrs); // Outer attribute scope
if let ItemKind::Mod(..) = item.kind {
- let old_mod = mem::replace(&mut self.current_mod, self.resolver.local_def_id(item.id));
+ let module_def_id = self.resolver.local_def_id(item.id).to_def_id();
+ let module = self.resolver.expect_module(module_def_id);
+ let old_module = mem::replace(&mut self.parent_scope.module, module);
+ let old_macro_rules = self.parent_scope.macro_rules;
self.resolve_doc_links_local(&item.attrs); // Inner attribute scope
- self.process_module_children_or_reexports(self.current_mod.to_def_id());
+ self.process_module_children_or_reexports(module_def_id);
visit::walk_item(self, item);
- self.current_mod = old_mod;
+ if item
+ .attrs
+ .iter()
+ .all(|attr| !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape))
+ {
+ self.parent_scope.macro_rules = old_macro_rules;
+ }
+ self.parent_scope.module = old_module;
} else {
- match item.kind {
+ match &item.kind {
ItemKind::Trait(..) => {
self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id());
}
ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
}
+ ItemKind::MacroDef(macro_def) if macro_def.macro_rules => {
+ self.parent_scope.macro_rules =
+ self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id));
+ }
_ => {}
}
visit::walk_item(self, item);
visit::walk_field_def(self, field)
}
+ fn visit_block(&mut self, block: &ast::Block) {
+ let old_macro_rules = self.parent_scope.macro_rules;
+ visit::walk_block(self, block);
+ self.parent_scope.macro_rules = old_macro_rules;
+ }
+
// NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
// then this will have to implement other visitor methods too.
}
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_local_trait_impls");
let mut attr_buf = Vec::new();
for &impl_def_id in all_trait_impls.iter().take_while(|def_id| def_id.is_local()) {
- let mut parent = cx.tcx.parent(impl_def_id);
+ let mut parent = Some(cx.tcx.parent(impl_def_id));
while let Some(did) = parent {
attr_buf.extend(
cx.tcx
})
.cloned(),
);
- parent = cx.tcx.parent(did);
+ parent = cx.tcx.opt_parent(did);
}
inline::build_impl(cx, None, impl_def_id, Some(&attr_buf), &mut new_items_local);
attr_buf.clear();
--- /dev/null
+// compile-flags: -O
+// only-x86_64
+// ignore-debug
+
+#![crate_type = "lib"]
+
+// CHECK-LABEL: @vec_zero_scalar
+#[no_mangle]
+pub fn vec_zero_scalar(n: usize) -> Vec<i32> {
+ // CHECK-NOT: __rust_alloc(
+ // CHECK: __rust_alloc_zeroed(
+ // CHECK-NOT: __rust_alloc(
+ vec![0; n]
+}
+
+// CHECK-LABEL: @vec_zero_rgb48
+#[no_mangle]
+pub fn vec_zero_rgb48(n: usize) -> Vec<[u16; 3]> {
+ // CHECK-NOT: __rust_alloc(
+ // CHECK: __rust_alloc_zeroed(
+ // CHECK-NOT: __rust_alloc(
+ vec![[0, 0, 0]; n]
+}
+
+// CHECK-LABEL: @vec_zero_array_32
+#[no_mangle]
+pub fn vec_zero_array_32(n: usize) -> Vec<[i64; 32]> {
+ // CHECK-NOT: __rust_alloc(
+ // CHECK: __rust_alloc_zeroed(
+ // CHECK-NOT: __rust_alloc(
+ vec![[0_i64; 32]; n]
+}
StorageDead(_6); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
_5 = const (); // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
goto -> bb2; // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
-+ }
-+
-+ bb8 (cleanup): {
-+ resume; // scope 0 at $DIR/derefer_complex_case.rs:3:1: 5:2
+ }
+
+ bb8 (cleanup): {
+ resume; // scope 0 at $DIR/derefer_complex_case.rs:3:1: 5:2
}
}
StorageDead(_2); // scope 1 at $DIR/derefer_terminator_test.rs:10:1: 10:2
StorageDead(_1); // scope 0 at $DIR/derefer_terminator_test.rs:10:1: 10:2
return; // scope 0 at $DIR/derefer_terminator_test.rs:10:2: 10:2
-+ }
-+
-+ bb6 (cleanup): {
-+ resume; // scope 0 at $DIR/derefer_terminator_test.rs:2:1: 10:2
+ }
+
+ bb6 (cleanup): {
+ resume; // scope 0 at $DIR/derefer_terminator_test.rs:2:1: 10:2
}
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/derefer_test.rs:3:9: 3:14
- Deinit(_1); // scope 0 at $DIR/derefer_test.rs:3:17: 3:24
- (_1.0: i32) = const 42_i32; // scope 0 at $DIR/derefer_test.rs:3:17: 3:24
- (_1.1: i32) = const 43_i32; // scope 0 at $DIR/derefer_test.rs:3:17: 3:24
+ _1 = (const 42_i32, const 43_i32); // scope 0 at $DIR/derefer_test.rs:3:17: 3:24
StorageLive(_2); // scope 1 at $DIR/derefer_test.rs:4:9: 4:14
StorageLive(_3); // scope 1 at $DIR/derefer_test.rs:4:22: 4:28
_3 = &mut _1; // scope 1 at $DIR/derefer_test.rs:4:22: 4:28
- Deinit(_2); // scope 1 at $DIR/derefer_test.rs:4:17: 4:29
- (_2.0: i32) = const 99_i32; // scope 1 at $DIR/derefer_test.rs:4:17: 4:29
- (_2.1: &mut (i32, i32)) = move _3; // scope 1 at $DIR/derefer_test.rs:4:17: 4:29
+ _2 = (const 99_i32, move _3); // scope 1 at $DIR/derefer_test.rs:4:17: 4:29
StorageDead(_3); // scope 1 at $DIR/derefer_test.rs:4:28: 4:29
StorageLive(_4); // scope 2 at $DIR/derefer_test.rs:5:9: 5:10
- _4 = &mut ((*(_2.1: &mut (i32, i32))).0: i32); // scope 2 at $DIR/derefer_test.rs:5:13: 5:26
StorageDead(_2); // scope 1 at $DIR/derefer_test.rs:7:1: 7:2
StorageDead(_1); // scope 0 at $DIR/derefer_test.rs:7:1: 7:2
return; // scope 0 at $DIR/derefer_test.rs:7:2: 7:2
-+ }
-+
-+ bb1 (cleanup): {
-+ resume; // scope 0 at $DIR/derefer_test.rs:2:1: 7:2
+ }
+
+ bb1 (cleanup): {
+ resume; // scope 0 at $DIR/derefer_test.rs:2:1: 7:2
}
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/derefer_test_multiple.rs:3:9: 3:14
- Deinit(_1); // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25
- (_1.0: i32) = const 42_i32; // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25
- (_1.1: i32) = const 43_i32; // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25
+ _1 = (const 42_i32, const 43_i32); // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25
StorageLive(_2); // scope 1 at $DIR/derefer_test_multiple.rs:4:9: 4:14
StorageLive(_3); // scope 1 at $DIR/derefer_test_multiple.rs:4:22: 4:28
_3 = &mut _1; // scope 1 at $DIR/derefer_test_multiple.rs:4:22: 4:28
- Deinit(_2); // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29
- (_2.0: i32) = const 99_i32; // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29
- (_2.1: &mut (i32, i32)) = move _3; // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29
+ _2 = (const 99_i32, move _3); // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29
StorageDead(_3); // scope 1 at $DIR/derefer_test_multiple.rs:4:28: 4:29
StorageLive(_4); // scope 2 at $DIR/derefer_test_multiple.rs:5:9: 5:14
StorageLive(_5); // scope 2 at $DIR/derefer_test_multiple.rs:5:22: 5:28
_5 = &mut _2; // scope 2 at $DIR/derefer_test_multiple.rs:5:22: 5:28
- Deinit(_4); // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29
- (_4.0: i32) = const 11_i32; // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29
- (_4.1: &mut (i32, &mut (i32, i32))) = move _5; // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29
+ _4 = (const 11_i32, move _5); // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29
StorageDead(_5); // scope 2 at $DIR/derefer_test_multiple.rs:5:28: 5:29
StorageLive(_6); // scope 3 at $DIR/derefer_test_multiple.rs:6:9: 6:14
StorageLive(_7); // scope 3 at $DIR/derefer_test_multiple.rs:6:22: 6:28
_7 = &mut _4; // scope 3 at $DIR/derefer_test_multiple.rs:6:22: 6:28
- Deinit(_6); // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29
- (_6.0: i32) = const 13_i32; // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29
- (_6.1: &mut (i32, &mut (i32, &mut (i32, i32)))) = move _7; // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29
+ _6 = (const 13_i32, move _7); // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29
StorageDead(_7); // scope 3 at $DIR/derefer_test_multiple.rs:6:28: 6:29
StorageLive(_8); // scope 4 at $DIR/derefer_test_multiple.rs:7:9: 7:10
- _8 = &mut ((*((*((*(_6.1: &mut (i32, &mut (i32, &mut (i32, i32))))).1: &mut (i32, &mut (i32, i32)))).1: &mut (i32, i32))).1: i32); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
StorageDead(_2); // scope 1 at $DIR/derefer_test_multiple.rs:9:1: 9:2
StorageDead(_1); // scope 0 at $DIR/derefer_test_multiple.rs:9:1: 9:2
return; // scope 0 at $DIR/derefer_test_multiple.rs:9:2: 9:2
-+ }
-+
-+ bb1 (cleanup): {
-+ resume; // scope 0 at $DIR/derefer_test_multiple.rs:2:1: 9:2
+ }
+
+ bb1 (cleanup): {
+ resume; // scope 0 at $DIR/derefer_test_multiple.rs:2:1: 9:2
}
}
+ bb3: {
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch.rs:8:1: 8:2
return; // scope 0 at $DIR/early_otherwise_branch.rs:8:2: 8:2
- }
-
-- bb5 (cleanup): {
-- resume; // scope 0 at $DIR/early_otherwise_branch.rs:3:1: 8:2
++ }
++
+ bb4: {
+ StorageDead(_11); // scope 0 at $DIR/early_otherwise_branch.rs:4:5: 4:17
+ switchInt(_7) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:4:5: 4:17
+ bb4: {
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch.rs:17:1: 17:2
return; // scope 0 at $DIR/early_otherwise_branch.rs:17:2: 17:2
- }
-
-- bb7 (cleanup): {
-- resume; // scope 0 at $DIR/early_otherwise_branch.rs:11:1: 17:2
++ }
++
+ bb5: {
+ StorageDead(_12); // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+ switchInt(_8) -> [0_isize: bb3, 1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+ bb3: {
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch.rs:26:1: 26:2
return; // scope 0 at $DIR/early_otherwise_branch.rs:26:2: 26:2
- }
-
-- bb5 (cleanup): {
-- resume; // scope 0 at $DIR/early_otherwise_branch.rs:21:1: 26:2
++ }
++
+ bb4: {
+ StorageDead(_11); // scope 0 at $DIR/early_otherwise_branch.rs:22:5: 22:17
+ switchInt(_7) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:22:5: 22:17
+ bb4: {
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:9:1: 9:2
return; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:9:2: 9:2
- }
-
-- bb6 (cleanup): {
-- resume; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:4:1: 9:2
++ }
++
+ bb5: {
+ StorageDead(_15); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:5:5: 5:20
+ switchInt(_10) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:5:5: 5:20
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:14:1: 14:2
return; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:14:2: 14:2
}
-
- bb9 (cleanup): {
- resume; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:7:1: 14:2
- }
}
bb5: {
return; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:27:2: 27:2
}
-
- bb6 (cleanup): {
- resume; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:18:1: 27:2
- }
}
bb4: {
return; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:14:2: 14:2
}
-
- bb5 (cleanup): {
- resume; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:12:1: 14:2
- }
}
StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:17:25: 17:26
return; // scope 0 at $DIR/if-condition-int.rs:18:2: 18:2
}
-
- bb4 (cleanup): {
- resume; // scope 0 at $DIR/if-condition-int.rs:16:1: 18:2
- }
}
StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:53:34: 53:35
return; // scope 0 at $DIR/if-condition-int.rs:54:2: 54:2
}
-
- bb4 (cleanup): {
- resume; // scope 0 at $DIR/if-condition-int.rs:52:1: 54:2
- }
}
StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:49:1: 49:2
return; // scope 0 at $DIR/if-condition-int.rs:49:2: 49:2
}
-
- bb4 (cleanup): {
- resume; // scope 0 at $DIR/if-condition-int.rs:43:1: 49:2
- }
}
StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:21:32: 21:33
return; // scope 0 at $DIR/if-condition-int.rs:22:2: 22:2
}
-
- bb4 (cleanup): {
- resume; // scope 0 at $DIR/if-condition-int.rs:20:1: 22:2
- }
}
StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:25:31: 25:32
return; // scope 0 at $DIR/if-condition-int.rs:26:2: 26:2
}
-
- bb4 (cleanup): {
- resume; // scope 0 at $DIR/if-condition-int.rs:24:1: 26:2
- }
}
StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:39:5: 39:6
return; // scope 0 at $DIR/if-condition-int.rs:40:2: 40:2
}
-
- bb7 (cleanup): {
- resume; // scope 0 at $DIR/if-condition-int.rs:32:1: 40:2
- }
}
StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:29:32: 29:33
return; // scope 0 at $DIR/if-condition-int.rs:30:2: 30:2
}
-
- bb4 (cleanup): {
- resume; // scope 0 at $DIR/if-condition-int.rs:28:1: 30:2
- }
}
StorageDead(_2); // scope 0 at $DIR/if-condition-int.rs:12:31: 12:32
return; // scope 0 at $DIR/if-condition-int.rs:13:2: 13:2
}
-
- bb4 (cleanup): {
- resume; // scope 0 at $DIR/if-condition-int.rs:11:1: 13:2
- }
}
scope 2 {
}
+ scope 3 (inlined Vec::<u32>::new) { // at $DIR/inline-into-box-place.rs:8:33: 8:43
++ let mut _8: alloc::raw_vec::RawVec<u32>; // in scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ }
bb0: {
- (*_5) = Vec::<u32>::new() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43
+ StorageLive(_7); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43
+ _7 = &mut (*_5); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43
-+ Deinit((*_7)); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-+ ((*_7).0: alloc::raw_vec::RawVec<u32>) = const alloc::raw_vec::RawVec::<u32> { ptr: Unique::<u32> { pointer: NonNull::<u32> { pointer: {0x4 as *const u32} }, _marker: PhantomData::<u32> }, cap: 0_usize, alloc: std::alloc::Global }; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ StorageLive(_8); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ _8 = const alloc::raw_vec::RawVec::<u32> { ptr: Unique::<u32> { pointer: NonNull::<u32> { pointer: {0x4 as *const u32} }, _marker: PhantomData::<u32> }, cap: 0_usize, alloc: std::alloc::Global }; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
// mir::Constant
- // + span: $DIR/inline-into-box-place.rs:8:33: 8:41
- // + user_ty: UserType(1)
+ // + span: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ // + user_ty: UserType(0)
+ // + literal: Const { ty: alloc::raw_vec::RawVec<u32>, val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 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 } }) }
++ Deinit((*_7)); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ ((*_7).0: alloc::raw_vec::RawVec<u32>) = move _8; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ ((*_7).1: usize) = const 0_usize; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ StorageDead(_8); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ StorageDead(_7); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43
_1 = move _5; // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43
StorageDead(_5); // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43
scope 2 {
}
+ scope 3 (inlined Vec::<u32>::new) { // at $DIR/inline-into-box-place.rs:8:33: 8:43
++ let mut _8: alloc::raw_vec::RawVec<u32>; // in scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ }
bb0: {
- (*_5) = Vec::<u32>::new() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43
+ StorageLive(_7); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43
+ _7 = &mut (*_5); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43
-+ Deinit((*_7)); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-+ ((*_7).0: alloc::raw_vec::RawVec<u32>) = const alloc::raw_vec::RawVec::<u32> { ptr: Unique::<u32> { pointer: NonNull::<u32> { pointer: {0x4 as *const u32} }, _marker: PhantomData::<u32> }, cap: 0_usize, alloc: std::alloc::Global }; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ StorageLive(_8); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ _8 = const alloc::raw_vec::RawVec::<u32> { ptr: Unique::<u32> { pointer: NonNull::<u32> { pointer: {0x4 as *const u32} }, _marker: PhantomData::<u32> }, cap: 0_usize, alloc: std::alloc::Global }; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
// mir::Constant
- // + span: $DIR/inline-into-box-place.rs:8:33: 8:41
- // + user_ty: UserType(1)
+ // + span: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ // + user_ty: UserType(0)
+ // + literal: Const { ty: alloc::raw_vec::RawVec<u32>, val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [65535], len: Size { raw: 16 } }, align: Align { pow2: 3 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
++ Deinit((*_7)); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ ((*_7).0: alloc::raw_vec::RawVec<u32>) = move _8; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ ((*_7).1: usize) = const 0_usize; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ StorageDead(_8); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ StorageDead(_7); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43
_1 = move _5; // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43
StorageDead(_5); // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43
StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:11:5: 11:6
return; // scope 0 at $DIR/lower_array_len.rs:12:2: 12:2
}
-
- bb6 (cleanup): {
- resume; // scope 0 at $DIR/lower_array_len.rs:6:1: 12:2
- }
}
StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:24:5: 24:6
return; // scope 0 at $DIR/lower_array_len.rs:25:2: 25:2
}
-
- bb7 (cleanup): {
- resume; // scope 0 at $DIR/lower_array_len.rs:17:1: 25:2
- }
}
StorageDead(_2); // scope 0 at $DIR/lower_array_len.rs:31:13: 31:14
return; // scope 0 at $DIR/lower_array_len.rs:32:2: 32:2
}
-
- bb2 (cleanup): {
- resume; // scope 0 at $DIR/lower_array_len.rs:30:1: 32:2
- }
}
StorageDead(_2); // scope 0 at $DIR/lower_array_len.rs:38:13: 38:14
return; // scope 0 at $DIR/lower_array_len.rs:39:2: 39:2
}
-
- bb2 (cleanup): {
- resume; // scope 0 at $DIR/lower_array_len.rs:37:1: 39:2
- }
}
StorageDead(_3); // scope 0 at $DIR/lower_slice_len.rs:9:5: 9:6
return; // scope 0 at $DIR/lower_slice_len.rs:10:2: 10:2
}
-
- bb6 (cleanup): {
- resume; // scope 0 at $DIR/lower_slice_len.rs:4:1: 10:2
- }
}
- StorageDead(_2); // scope 0 at $DIR/nrvo-simple.rs:8:1: 8:2
return; // scope 0 at $DIR/nrvo-simple.rs:8:2: 8:2
}
-
- bb2 (cleanup): {
- resume; // scope 0 at $DIR/nrvo-simple.rs:4:1: 8:2
- }
}
bb0: {
_2 = discriminant(_1); // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:11: 22:12
- switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:5: 22:12
-+ switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb6]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:5: 22:12
++ switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb5]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:5: 22:12
}
bb1: {
bb4: {
return; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:27:2: 27:2
- }
-
- bb5 (cleanup): {
- resume; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:21:1: 27:2
+ }
+
-+ bb6: {
++ bb5: {
+ unreachable; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:25:14: 25:15
}
}
bb4: {
return; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:18:2: 18:2
}
-
- bb5 (cleanup): {
- resume; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:12:1: 18:2
- }
}
_0 = const (); // scope 0 at $DIR/unreachable.rs:19:6: 19:6
StorageDead(_1); // scope 0 at $DIR/unreachable.rs:20:1: 20:2
return; // scope 0 at $DIR/unreachable.rs:20:2: 20:2
-- }
--
-- bb7 (cleanup): {
-- resume; // scope 0 at $DIR/unreachable.rs:8:1: 20:2
}
}
StorageDead(_1); // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2
StorageDead(_2); // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2
return; // scope 0 at $DIR/unreachable_diverging.rs:20:2: 20:2
-- }
--
-- bb7 (cleanup): {
-- resume; // scope 0 at $DIR/unreachable_diverging.rs:12:1: 20:2
}
}
--- /dev/null
+// pp-exact
+#![feature(yeet_expr)]
+
+fn yeet_no_expr() -> Option<String> { do yeet }
+
+fn yeet_no_expr_with_semicolon() -> Option<String> { do yeet; }
+
+fn yeet_with_expr() -> Result<String, i32> { do yeet 1 + 2 }
+
+fn yeet_with_expr_with_semicolon() -> Result<String, i32> { do yeet 1 + 2; }
+
+fn main() {}
_0 = move (_1.0: i32); // scope 0 at main.rs:5:5: 5:10
return; // scope 0 at main.rs:6:2: 6:2
}
-
- bb2 (cleanup): {
- resume; // scope 0 at main.rs:4:1: 6:2
- }
}
fn main() -> () {
// First, we check that the search results are hidden when the Escape key is pressed.
write: (".search-input", "test")
wait-for: "#search h1" // The search element is empty before the first search
-assert-attribute: ("#search", {"class": "content"})
+// Check that the currently displayed element is search.
+wait-for: "#alternative-display #search"
assert-attribute: ("#main-content", {"class": "content hidden"})
assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH)
press-key: "Escape"
-assert-attribute: ("#search", {"class": "content hidden"})
+// Checks that search is no longer in the displayed content.
+wait-for: "#not-displayed #search"
+assert-false: "#alternative-display #search"
assert-attribute: ("#main-content", {"class": "content"})
assert-document-property: ({"URL": "index.html"}, [ENDS_WITH])
// Check that focusing the search input brings back the search results
focus: ".search-input"
-assert-attribute: ("#search", {"class": "content"})
+wait-for: "#alternative-display #search"
assert-attribute: ("#main-content", {"class": "content hidden"})
assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH)
assert-document-property: ({"URL": "index.html?search=test"}, [ENDS_WITH])
assert-attribute: ("#help", {"class": ""})
press-key: "Escape"
+wait-for: "#alternative-display #search"
assert-attribute: ("#help", {"class": "hidden"})
-assert-attribute: ("#search", {"class": "content"})
assert-attribute: ("#main-content", {"class": "content hidden"})
assert-document-property: ({"URL": "index.html?search=test"}, [ENDS_WITH])
assert: "#results a:focus"
press-key: "Escape"
assert-attribute: ("#help", {"class": "hidden"})
-assert-attribute: ("#search", {"class": "content hidden"})
+wait-for: "#not-displayed #search"
+assert-false: "#alternative-display #search"
assert-attribute: ("#main-content", {"class": "content"})
--- /dev/null
+// This test ensures that the settings menu display is working as expected.
+goto: file://|DOC_PATH|/test_docs/index.html
+// First, we check that the settings page doesn't exist.
+assert-false: "#settings"
+// We now click on the settings button.
+click: "#settings-menu"
+wait-for: "#settings"
+assert: "#main-content.hidden"
+assert-css: ("#settings", {"display": "block"})
+// Let's close it by clicking on the same button.
+click: "#settings-menu"
+assert-false: "#alternative-display #settings"
+assert: "#not-displayed #settings"
+assert: "#main-content:not(.hidden)"
+
+// Let's open and then close it again with the "close settings" button.
+click: "#settings-menu"
+wait-for: "#alternative-display #settings"
+assert: "#main-content.hidden"
+click: "#back"
+wait-for: "#not-displayed #settings"
+assert: "#main-content:not(.hidden)"
+
+// Let's check that pressing "ESCAPE" is closing it.
+click: "#settings-menu"
+wait-for: "#alternative-display #settings"
+press-key: "Escape"
+wait-for: "#not-displayed #settings"
+assert: "#main-content:not(.hidden)"
+
+// Let's click on it when the search results are displayed.
+focus: ".search-input"
+write: "test"
+wait-for: "#alternative-display #search"
+click: "#settings-menu"
+wait-for: "#alternative-display #settings"
+assert: "#not-displayed #search"
+assert: "#main-content.hidden"
+
+// Now let's check the content of the settings menu.
+local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"}
+reload:
+click: "#settings-menu"
+wait-for: "#settings"
+
+// We check that the "Use system theme" is disabled.
+assert-property: ("#use-system-theme", {"checked": "false"})
+assert: "//*[@class='setting-line']/*[text()='Use system theme']"
+// Meaning that only the "theme" menu is showing up.
+assert: ".setting-line:not(.hidden) #theme"
+assert: ".setting-line.hidden #preferred-dark-theme"
+assert: ".setting-line.hidden #preferred-light-theme"
+
+// We check that the correct theme is selected.
+assert-property: ("#theme .choices #theme-dark", {"checked": "true"})
+
+// We now switch the display.
+click: "#use-system-theme"
+// Wait for the hidden element to show up.
+wait-for: ".setting-line:not(.hidden) #preferred-dark-theme"
+assert: ".setting-line:not(.hidden) #preferred-light-theme"
+// Check that the theme picking is hidden.
+assert: ".setting-line.hidden #theme"
+
+// We check their text as well.
+assert-text: ("#preferred-dark-theme .setting-name", "Preferred dark theme")
+assert-text: ("#preferred-light-theme .setting-name", "Preferred light theme")
wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" })
goto: file://|DOC_PATH|/settings.html
+wait-for: "#settings"
click: "#theme-light"
wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" })
assert-local-storage: { "rustdoc-theme": "light" }
// Ensures that the theme is working when going back in history.
goto: file://|DOC_PATH|/test_docs/index.html
// Set the theme to dark.
-local-storage: {"rustdoc-theme": "dark", "rustdoc-preferred-dark-theme": "dark", "rustdoc-use-system-theme": "false"}
+local-storage: {
+ "rustdoc-theme": "dark",
+ "rustdoc-preferred-dark-theme": "dark",
+ "rustdoc-use-system-theme": "false",
+}
// We reload the page so the local storage settings are being used.
reload:
assert-css: ("body", { "background-color": "rgb(53, 53, 53)" })
assert-local-storage: { "rustdoc-theme": "dark" }
// Now we go to the settings page.
-click: "#settings-menu"
-wait-for: ".settings"
+goto: file://|DOC_PATH|/settings.html
+wait-for: "#settings"
// We change the theme to "light".
click: "#theme-light"
wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" })
// We go back in history.
history-go-back:
// Confirm that we're not on the settings page.
-assert-false: ".settings"
+assert-false: "#settings"
// Check that the current theme is still "light".
assert-css: ("body", { "background-color": "rgb(255, 255, 255)" })
assert-local-storage: { "rustdoc-theme": "light" }
// check-pass
// compile-flags:--test
+// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME"
// This test ensures that no code block is detected in the doc comments.
running 0 tests
-test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
--- /dev/null
+// Traits in scope are collected for doc links in field attributes.
+
+// check-pass
+// aux-build: assoc-field-dep.rs
+
+extern crate assoc_field_dep;
+pub use assoc_field_dep::*;
+
+#[derive(Clone)]
+pub struct Struct;
+
+pub mod mod1 {
+ pub struct Fields {
+ /// [crate::Struct::clone]
+ pub field: u8,
+ }
+}
+
+pub mod mod2 {
+ pub enum Fields {
+ V {
+ /// [crate::Struct::clone]
+ field: u8,
+ },
+ }
+}
--- /dev/null
+#[derive(Clone)]
+pub struct Struct;
+
+pub mod dep_mod1 {
+ pub struct Fields {
+ /// [crate::Struct::clone]
+ pub field: u8,
+ }
+}
+
+pub mod dep_mod2 {
+ pub enum Fields {
+ V {
+ /// [crate::Struct::clone]
+ field: u8,
+ },
+ }
+}
--- /dev/null
+// `macro_rules` scopes are respected during doc link resolution.
+
+// compile-flags: --document-private-items
+
+#![deny(rustdoc::broken_intra_doc_links)]
+
+mod no_escape {
+ macro_rules! before_but_limited_to_module {
+ () => {};
+ }
+}
+
+/// [before_but_limited_to_module] FIXME: This error should be reported
+// ERROR unresolved link to `before_but_limited_to_module`
+/// [after] FIXME: This error should be reported
+// ERROR unresolved link to `after`
+/// [str] FIXME: This error shouldn not be reported
+//~^ ERROR `str` is both a builtin type and a macro
+fn check() {}
+
+macro_rules! after {
+ () => {};
+}
+
+macro_rules! str {
+ () => {};
+}
--- /dev/null
+error: `str` is both a builtin type and a macro
+ --> $DIR/macro-rules-error.rs:17:6
+ |
+LL | /// [str] FIXME: This error shouldn not be reported
+ | ^^^ ambiguous link
+ |
+note: the lint level is defined here
+ --> $DIR/macro-rules-error.rs:5:9
+ |
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: to link to the builtin type, prefix with `prim@`
+ |
+LL | /// [prim@str] FIXME: This error shouldn not be reported
+ | +++++
+help: to link to the macro, add an exclamation mark
+ |
+LL | /// [str!] FIXME: This error shouldn not be reported
+ | +
+
+error: aborting due to previous error
+
/// [foo!]
pub fn baz() {}
+
+#[macro_use]
+mod macros {
+ macro_rules! escaping {
+ () => {};
+ }
+}
+
+pub mod inner {
+ /// [foo!]
+ /// [escaping]
+ pub fn baz() {
+ foo!();
+ }
+}
--- /dev/null
+fn f(_: usize, _: &usize, _: usize) {}
+
+fn arg<T>() -> T { todo!() }
+
+fn main() {
+ let x = arg(); // `x` must be inferred
+ // The reference on `&x` is important to reproduce the ICE
+ f(&x, ""); //~ ERROR this function takes 3 arguments but 2 arguments were supplied
+}
--- /dev/null
+error[E0061]: this function takes 3 arguments but 2 arguments were supplied
+ --> $DIR/issue-96638.rs:8:5
+ |
+LL | f(&x, "");
+ | ^ -- an argument of type `usize` is missing
+ |
+note: function defined here
+ --> $DIR/issue-96638.rs:1:4
+ |
+LL | fn f(_: usize, _: &usize, _: usize) {}
+ | ^ -------- --------- --------
+help: provide the argument
+ |
+LL | f({usize}, &x, {usize});
+ | ~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0061`.
--- /dev/null
+// check-pass
+pub trait Hasher {
+ type State;
+
+ fn hash<T: Hash<
+ <Self as Hasher>::State
+ >>(&self, value: &T) -> u64;
+}
+
+pub trait Hash<S> {
+ fn hash(&self, state: &mut S);
+}
+
+fn main() {}
-{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"kind":{"variant":"Interpolated","fields":[{"variant":"NtExpr","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}}]}]},"span":{"lo":0,"hi":0}}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"spans":{"inner_span":{"lo":0,"hi":0},"inject_use_span":{"lo":0,"hi":0}},"id":0,"is_placeholder":false}
+{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"variant":"Ast","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}}]}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"spans":{"inner_span":{"lo":0,"hi":0},"inject_use_span":{"lo":0,"hi":0}},"id":0,"is_placeholder":false}
-{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"kind":{"variant":"Interpolated","fields":[{"variant":"NtExpr","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}}]}]},"span":{"lo":0,"hi":0}}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"rust_2015","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"spans":{"inner_span":{"lo":0,"hi":0},"inject_use_span":{"lo":0,"hi":0}},"id":0,"is_placeholder":false}
+{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"variant":"Ast","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}}]}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"rust_2015","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"spans":{"inner_span":{"lo":0,"hi":0},"inject_use_span":{"lo":0,"hi":0}},"id":0,"is_placeholder":false}
| | expected one of `extern`, `fn`, or `unsafe`
| help: `const` must come before `async`: `const async`
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to previous error
LL | }
| - the item list ends here
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: expected one of `extern` or `fn`, found keyword `async`
--> $DIR/no-unsafe-async.rs:11:8
| | expected one of `extern` or `fn`
| help: `async` must come before `unsafe`: `async unsafe`
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to 2 previous errors
#![u=||{static d=||1;}]
-//~^ unexpected token
+//~^ unexpected expression
//~| cannot find attribute `u` in this scope
//~| missing type for `static` item
#![a={impl std::ops::Neg for i8 {}}]
-//~^ ERROR unexpected token
+//~^ ERROR unexpected expression
//~| ERROR cannot find attribute `a` in this scope
//~| ERROR `main` function not found in crate `issue_90873`
-error: unexpected token: `||
+error: unexpected expression: `||
{
static d: _ = || 1;
}`
LL | #![u=||{static d=||1;}]
| ^^^^^^^^^^^^^^^^^
-error: unexpected token: `{
+error: unexpected expression: `{
impl std::ops::Neg for i8 {}
}`
--> $DIR/issue-90873.rs:6:6
// FIXME?: `bar` here expands before `stringify` has a chance to expand.
// `#[rustc_dummy = ...]` is validated and dropped during expansion of `bar`,
-// the "unexpected token" errors comes from the validation.
-#[rustc_dummy = stringify!(b)] //~ ERROR unexpected token: `stringify!(b)`
+// the "unexpected expression" errors comes from the validation.
+#[rustc_dummy = stringify!(b)] //~ ERROR unexpected expression: `stringify!(b)`
bar!();
fn main() {}
-error: unexpected token: `stringify!(b)`
+error: unexpected expression: `stringify!(b)`
--> $DIR/key-value-expansion-on-mac.rs:11:17
|
LL | #[rustc_dummy = stringify!(b)]
// Any expressions containing macro call `X` that's more complex than `X` itself.
// Parentheses will work.
-bug!((column!())); //~ ERROR unexpected token: `(7u32)`
+bug!((column!())); //~ ERROR unexpected expression: `(7u32)`
// Original test case.
macro_rules! bug {
() => {
- bug!("bug" + stringify!(found)); //~ ERROR unexpected token: `"bug" + "found"`
+ bug!("bug" + stringify!(found)); //~ ERROR unexpected expression: `"bug" + "found"`
};
($test:expr) => {
#[doc = $test]
macro_rules! some_macro {
($t1: ty) => {
doc_comment! {format!("{coor}", coor = stringify!($t1)).as_str()}
- //~^ ERROR unexpected token: `{
+ //~^ ERROR unexpected expression: `{
};
}
-error: unexpected token: `(7u32)`
+error: unexpected expression: `(7u32)`
--> $DIR/key-value-expansion.rs:21:6
|
LL | bug!((column!()));
| ^^^^^^^^^^^
-error: unexpected token: `"bug" + "found"`
+error: unexpected expression: `"bug" + "found"`
--> $DIR/key-value-expansion.rs:27:14
|
LL | bug!("bug" + stringify!(found));
|
= note: this error originates in the macro `bug` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: unexpected token: `{
+error: unexpected expression: `{
let res =
::alloc::fmt::format(::core::fmt::Arguments::new_v1(&[""],
&[::core::fmt::ArgumentV1::new_display(&"u8")]));
--- /dev/null
+// run-pass
+fn main() {
+ let mut t = [1; 2];
+ t = [t[1] * 2, t[0] * 2];
+ assert_eq!(&t[..], &[2, 2]);
+}
--- /dev/null
+// run-pass
+struct A {
+ pub x: u32,
+ pub y: u32,
+}
+
+fn main() {
+ let mut a = A { x: 1, y: 1 };
+ a = A { x: a.y * 2, y: a.x * 2 };
+ assert_eq!(a.x, 2);
+ assert_eq!(a.y, 2);
+}
--- /dev/null
+// run-pass
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#[derive(Debug)]
+enum Foo {
+ Bar(u32, u32),
+ Baz(&'static u32, &'static u32)
+}
+
+static NUM: u32 = 100;
+
+fn main () {
+ let mut b = Foo::Baz(&NUM, &NUM);
+ b = Foo::Bar(f(&b), g(&b));
+}
+
+static FNUM: u32 = 1;
+
+fn f (b: &Foo) -> u32 {
+ FNUM
+}
+
+static GNUM: u32 = 2;
+
+fn g (b: &Foo) -> u32 {
+ GNUM
+}
--- /dev/null
+// run-pass
+#![allow(unreachable_code)]
+
+use std::sync::atomic::{AtomicBool, Ordering, AtomicUsize};
+
+struct Print(usize);
+
+impl Drop for Print {
+ fn drop(&mut self) {
+ println!("{}", self.0);
+ FOO[self.0].store(true, Ordering::Relaxed);
+ assert_eq!(BAR.fetch_sub(1, Ordering::Relaxed), self.0);
+ }
+}
+
+const A: Print = Print(0);
+const B: Print = Print(1);
+
+static FOO: [AtomicBool; 3] =
+ [AtomicBool::new(false), AtomicBool::new(false), AtomicBool::new(false)];
+static BAR: AtomicUsize = AtomicUsize::new(2);
+
+fn main() {
+ loop {
+ std::mem::forget(({ A }, B, Print(2), break));
+ }
+ for (i, b) in FOO.iter().enumerate() {
+ assert!(b.load(Ordering::Relaxed), "{} not set", i);
+ }
+ assert_eq!(BAR.fetch_add(1, Ordering::Relaxed), usize::max_value());
+}
- #![l=|x|[b;x ]] //~ ERROR unexpected token: `|x| [b; x]`
+ #![l=|x|[b;x ]] //~ ERROR unexpected expression: `|x| [b; x]`
//~^ ERROR cannot find attribute `l` in this scope
// notice the space at the start,
-error: unexpected token: `|x| [b; x]`
+error: unexpected expression: `|x| [b; x]`
--> $DIR/issue-90878-2.rs:1:7
|
LL | #![l=|x|[b;x ]]
--- /dev/null
+// check-pass
+
+struct S;
+
+enum E {
+ V,
+}
+
+type A = E;
+
+fn main() {
+ let mut a;
+
+ (S, a) = (S, ());
+
+ (E::V, a) = (E::V, ());
+
+ (<E>::V, a) = (E::V, ());
+ (A::V, a) = (E::V, ());
+}
+
+impl S {
+ fn check() {
+ let a;
+ (Self, a) = (S, ());
+ }
+}
+
+impl E {
+ fn check() {
+ let a;
+ (Self::V, a) = (E::V, ());
+ }
+}
--- /dev/null
+// compile-flags: --edition 2021
+
+pub fn demo() -> Option<i32> {
+ #[cfg(nope)]
+ {
+ do yeet //~ ERROR `do yeet` expression is experimental
+ }
+
+ Some(1)
+}
+
+#[cfg(nope)]
+pub fn alternative() -> Result<(), String> {
+ do yeet "hello"; //~ ERROR `do yeet` expression is experimental
+}
+
+fn main() {
+ demo();
+}
--- /dev/null
+error[E0658]: `do yeet` expression is experimental
+ --> $DIR/feature-gate-yeet_expr-in-cfg.rs:6:9
+ |
+LL | do yeet
+ | ^^^^^^^
+ |
+ = note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
+ = help: add `#![feature(yeet_expr)]` to the crate attributes to enable
+
+error[E0658]: `do yeet` expression is experimental
+ --> $DIR/feature-gate-yeet_expr-in-cfg.rs:14:5
+ |
+LL | do yeet "hello";
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
+ = help: add `#![feature(yeet_expr)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+// compile-flags: --edition 2018
+
+pub fn demo() -> Option<i32> {
+ do yeet //~ ERROR `do yeet` expression is experimental
+}
+
+pub fn main() -> Result<(), String> {
+ do yeet "hello"; //~ ERROR `do yeet` expression is experimental
+}
--- /dev/null
+error[E0658]: `do yeet` expression is experimental
+ --> $DIR/feature-gate-yeet_expr.rs:4:5
+ |
+LL | do yeet
+ | ^^^^^^^
+ |
+ = note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
+ = help: add `#![feature(yeet_expr)]` to the crate attributes to enable
+
+error[E0658]: `do yeet` expression is experimental
+ --> $DIR/feature-gate-yeet_expr.rs:8:5
+ |
+LL | do yeet "hello";
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
+ = help: add `#![feature(yeet_expr)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+// edition:2018
+
+default pub const async unsafe extern fn err() {} //~ ERROR `default` is not followed by an item
+//~^ ERROR expected item, found keyword `pub`
+
+pub default const async unsafe extern fn ok() {}
--- /dev/null
+error: `default` is not followed by an item
+ --> $DIR/keyword-order.rs:3:1
+ |
+LL | default pub const async unsafe extern fn err() {}
+ | ^^^^^^^ the `default` qualifier
+ |
+ = note: only `fn`, `const`, `type`, or `impl` items may be prefixed by `default`
+
+error: expected item, found keyword `pub`
+ --> $DIR/keyword-order.rs:3:9
+ |
+LL | default pub const async unsafe extern fn err() {}
+ | ^^^ expected item
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// edition:2021
+
+use std::iter;
+
+fn f<T>(data: &[T]) -> impl Iterator<Item = Vec> {
+ //~^ ERROR: missing generics for struct `Vec` [E0107]
+ iter::empty() //~ ERROR: type annotations needed [E0282]
+}
+
+fn g<T>(data: &[T], target: T) -> impl Iterator<Item = Vec<T>> {
+ //~^ ERROR: type annotations needed [E0282]
+ f(data).filter(|x| x == target)
+}
+
+fn main() {}
--- /dev/null
+error[E0107]: missing generics for struct `Vec`
+ --> $DIR/issue-92305.rs:5:45
+ |
+LL | fn f<T>(data: &[T]) -> impl Iterator<Item = Vec> {
+ | ^^^ expected at least 1 generic argument
+ |
+note: struct defined here, with at least 1 generic parameter: `T`
+ --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ |
+LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
+ | ^^^ -
+help: add missing generic argument
+ |
+LL | fn f<T>(data: &[T]) -> impl Iterator<Item = Vec<T>> {
+ | ~~~~~~
+
+error[E0282]: type annotations needed
+ --> $DIR/issue-92305.rs:7:5
+ |
+LL | iter::empty()
+ | ^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the function `empty`
+
+error[E0282]: type annotations needed
+ --> $DIR/issue-92305.rs:10:35
+ |
+LL | fn g<T>(data: &[T], target: T) -> impl Iterator<Item = Vec<T>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0107, E0282.
+For more information about an error, try `rustc --explain E0107`.
-error[E0284]: type annotations needed
- --> $DIR/question-mark-type-infer.rs:10:21
+error[E0282]: type annotations needed
+ --> $DIR/question-mark-type-infer.rs:10:30
|
LL | l.iter().map(f).collect()?
- | ^^^^^^^ cannot infer type
- |
- = note: cannot satisfy `<_ as Try>::Residual == _`
-help: consider specifying the type argument in the method call
- |
-LL | l.iter().map(f).collect::<B>()?
- | +++++
+ | ^ cannot infer type
error: aborting due to previous error
-For more information about this error, try `rustc --explain E0284`.
+For more information about this error, try `rustc --explain E0282`.
+++ /dev/null
-// run-pass
-fn main() {
- let mut t = [1; 2];
- t = [t[1] * 2, t[0] * 2];
- assert_eq!(&t[..], &[2, 2]);
-}
+++ /dev/null
-// run-pass
-struct A {
- pub x: u32,
- pub y: u32,
-}
-
-fn main() {
- let mut a = A { x: 1, y: 1 };
- a = A { x: a.y * 2, y: a.x * 2 };
- assert_eq!(a.x, 2);
- assert_eq!(a.y, 2);
-}
+++ /dev/null
-// run-pass
-#![allow(unused_variables)]
-#![allow(unused_assignments)]
-#[derive(Debug)]
-enum Foo {
- Bar(u32, u32),
- Baz(&'static u32, &'static u32)
-}
-
-static NUM: u32 = 100;
-
-fn main () {
- let mut b = Foo::Baz(&NUM, &NUM);
- b = Foo::Bar(f(&b), g(&b));
-}
-
-static FNUM: u32 = 1;
-
-fn f (b: &Foo) -> u32 {
- FNUM
-}
-
-static GNUM: u32 = 2;
-
-fn g (b: &Foo) -> u32 {
- GNUM
-}
+++ /dev/null
-// check-pass
-pub trait Hasher {
- type State;
-
- fn hash<T: Hash<
- <Self as Hasher>::State
- >>(&self, value: &T) -> u64;
-}
-
-pub trait Hash<S> {
- fn hash(&self, state: &mut S);
-}
-
-fn main() {}
| ^ the trait `From<{integer}>` is not implemented for `()`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
- = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Result<T, F> as FromResidual<Result<Infallible, E>>>
+ <Result<T, F> as FromResidual<Yeet<E>>>
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, {integer}>>` for `Result<i32, ()>`
error: aborting due to previous error
+++ /dev/null
-use std::mem;
-
-trait Misc {}
-
-fn size_of_copy<T: Copy+?Sized>() -> usize { mem::size_of::<T>() }
-
-fn main() {
- size_of_copy::<dyn Misc + Copy>();
- //~^ ERROR only auto traits can be used as additional traits in a trait object
- //~| ERROR only auto traits can be used as additional traits in a trait object
- //~| ERROR the trait bound `dyn Misc: Copy` is not satisfied
-}
+++ /dev/null
-error[E0225]: only auto traits can be used as additional traits in a trait object
- --> $DIR/issue-32963.rs:8:31
- |
-LL | size_of_copy::<dyn Misc + Copy>();
- | ---- ^^^^ additional non-auto trait
- | |
- | first non-auto trait
- |
- = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Misc + Copy {}`
- = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
-
-error[E0225]: only auto traits can be used as additional traits in a trait object
- --> $DIR/issue-32963.rs:8:31
- |
-LL | size_of_copy::<dyn Misc + Copy>();
- | ---- ^^^^ additional non-auto trait
- | |
- | first non-auto trait
- |
- = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Misc + Copy {}`
- = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
-
-error[E0277]: the trait bound `dyn Misc: Copy` is not satisfied
- --> $DIR/issue-32963.rs:8:5
- |
-LL | size_of_copy::<dyn Misc + Copy>();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `dyn Misc`
- |
-note: required by a bound in `size_of_copy`
- --> $DIR/issue-32963.rs:5:20
- |
-LL | fn size_of_copy<T: Copy+?Sized>() -> usize { mem::size_of::<T>() }
- | ^^^^ required by this bound in `size_of_copy`
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0225, E0277.
-For more information about an error, try `rustc --explain E0225`.
+++ /dev/null
-macro_rules! m { ($($t:tt)*) => { $($t)* } }
-
-fn main() {
- m!($t); //~ ERROR expected expression
-}
+++ /dev/null
-error: expected expression, found `$`
- --> $DIR/issue-35450.rs:4:8
- |
-LL | m!($t);
- | ^ expected expression
-
-error: aborting due to previous error
-
+++ /dev/null
-// check-pass
-// compile-flags: -Zsave-analysis
-
-#![feature(rustc_attrs)]
-#![allow(warnings)]
-
-#[derive(Debug)]
-struct Point {
-}
-
-struct NestedA<'a, 'b> {
- x: &'a NestedB<'b>
-}
-
-struct NestedB<'a> {
- x: &'a i32,
-}
-
-fn main() {
-}
+++ /dev/null
-// run-pass
-#![allow(dead_code)]
-
-pub fn main() {
- let _id: &Mat2<f64> = &Matrix::identity(1.0);
-}
-
-pub trait Index<Index,Result> { fn get(&self, _: Index) -> Result { panic!() } }
-pub trait Dimensional<T>: Index<usize, T> { }
-
-pub struct Mat2<T> { x: T }
-pub struct Vec2<T> { x: T }
-
-impl<T> Dimensional<Vec2<T>> for Mat2<T> { }
-impl<T> Index<usize, Vec2<T>> for Mat2<T> { }
-
-impl<T> Dimensional<T> for Vec2<T> { }
-impl<T> Index<usize, T> for Vec2<T> { }
-
-pub trait Matrix<T,V>: Dimensional<V> {
- fn identity(t:T) -> Self;
-}
-
-impl<T> Matrix<T, Vec2<T>> for Mat2<T> {
- fn identity(t:T) -> Mat2<T> { Mat2{ x: t } }
-}
+++ /dev/null
-trait T {
- fn f(&self, _: ()) {
- None::<()>.map(Self::f);
- }
- //~^^ ERROR function is expected to take a single 0-tuple as argument
-}
-
-fn main() {}
+++ /dev/null
-error[E0593]: function is expected to take a single 0-tuple as argument, but it takes 2 distinct arguments
- --> $DIR/issue-47706-trait.rs:3:24
- |
-LL | fn f(&self, _: ()) {
- | ------------------ takes 2 distinct arguments
-LL | None::<()>.map(Self::f);
- | --- ^^^^^^^ expected function that takes a single 0-tuple as argument
- | |
- | required by a bound introduced by this call
- |
-note: required by a bound in `Option::<T>::map`
- --> $SRC_DIR/core/src/option.rs:LL:COL
- |
-LL | F: ~const FnOnce(T) -> U,
- | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Option::<T>::map`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0593`.
+++ /dev/null
-pub struct Foo {
- foo: Option<i32>,
-}
-
-impl Foo {
- pub fn new(foo: Option<i32>, _: ()) -> Foo {
- Foo { foo }
- }
-
- pub fn map(self) -> Option<Foo> {
- self.foo.map(Foo::new)
- }
- //~^^ ERROR function is expected to take 1 argument, but it takes 2 arguments [E0593]
-}
-
-enum Qux {
- Bar(i32),
-}
-
-fn foo<F>(f: F)
-where
- F: Fn(),
-{
-}
-
-fn main() {
- foo(Qux::Bar);
-}
-//~^^ ERROR function is expected to take 0 arguments, but it takes 1 argument [E0593]
+++ /dev/null
-error[E0593]: function is expected to take 1 argument, but it takes 2 arguments
- --> $DIR/issue-47706.rs:11:22
- |
-LL | pub fn new(foo: Option<i32>, _: ()) -> Foo {
- | ------------------------------------------ takes 2 arguments
-...
-LL | self.foo.map(Foo::new)
- | --- ^^^^^^^^ expected function that takes 1 argument
- | |
- | required by a bound introduced by this call
- |
-note: required by a bound in `Option::<T>::map`
- --> $SRC_DIR/core/src/option.rs:LL:COL
- |
-LL | F: ~const FnOnce(T) -> U,
- | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Option::<T>::map`
-
-error[E0593]: function is expected to take 0 arguments, but it takes 1 argument
- --> $DIR/issue-47706.rs:27:9
- |
-LL | Bar(i32),
- | -------- takes 1 argument
-...
-LL | foo(Qux::Bar);
- | --- ^^^^^^^^ expected function that takes 0 arguments
- | |
- | required by a bound introduced by this call
- |
-note: required by a bound in `foo`
- --> $DIR/issue-47706.rs:22:8
- |
-LL | fn foo<F>(f: F)
- | --- required by a bound in this
-LL | where
-LL | F: Fn(),
- | ^^^^ required by this bound in `foo`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0593`.
+++ /dev/null
-error: lifetime may not live long enough
- --> $DIR/issue-52213.rs:3:20
- |
-LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
- | -- -- lifetime `'b` defined here
- | |
- | lifetime `'a` defined here
-LL | match (&t,) {
-LL | ((u,),) => u,
- | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
- |
- = help: consider adding the following bound: `'a: 'b`
-
-error: aborting due to previous error
-
+++ /dev/null
-fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
- match (&t,) { //~ ERROR cannot infer an appropriate lifetime
- ((u,),) => u,
- }
-}
-
-fn main() {
- let x = {
- let y = Box::new((42,));
- transmute_lifetime(&y)
- };
-
- println!("{}", x);
-}
+++ /dev/null
-error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
- --> $DIR/issue-52213.rs:2:11
- |
-LL | match (&t,) {
- | ^^^^^
- |
-note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
- --> $DIR/issue-52213.rs:1:23
- |
-LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
- | ^^
-note: ...so that the types are compatible
- --> $DIR/issue-52213.rs:2:11
- |
-LL | match (&t,) {
- | ^^^^^
- = note: expected `(&&(T,),)`
- found `(&&'a (T,),)`
-note: but, the lifetime must be valid for the lifetime `'b` as defined here...
- --> $DIR/issue-52213.rs:1:27
- |
-LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
- | ^^
-note: ...so that reference does not outlive borrowed content
- --> $DIR/issue-52213.rs:3:20
- |
-LL | ((u,),) => u,
- | ^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0495`.
+++ /dev/null
-enum A {
- StructWithFields { x: () },
- TupleWithFields(()),
- Struct {},
- Tuple(),
- Unit,
-}
-
-enum B {
- StructWithFields { x: () },
- TupleWithFields(()),
-}
-
-enum C {
- StructWithFields { x: () },
- TupleWithFields(()),
- Unit,
-}
-
-enum D {
- TupleWithFields(()),
- Unit,
-}
-
-fn main() {
- // Only variants without fields are suggested (and others mentioned in a note) where an enum
- // is used rather than a variant.
-
- A.foo();
- //~^ ERROR expected value, found enum `A`
- B.foo();
- //~^ ERROR expected value, found enum `B`
- C.foo();
- //~^ ERROR expected value, found enum `C`
- D.foo();
- //~^ ERROR expected value, found enum `D`
-
- // Only tuple variants are suggested in calls or tuple struct pattern matching.
-
- let x = A(3);
- //~^ ERROR expected function, tuple struct or tuple variant, found enum `A`
- if let A(3) = x { }
- //~^ ERROR expected tuple struct or tuple variant, found enum `A`
-}
+++ /dev/null
-error[E0423]: expected value, found enum `A`
- --> $DIR/issue-73427.rs:29:5
- |
-LL | A.foo();
- | ^
- |
-note: the enum is defined here
- --> $DIR/issue-73427.rs:1:1
- |
-LL | / enum A {
-LL | | StructWithFields { x: () },
-LL | | TupleWithFields(()),
-LL | | Struct {},
-LL | | Tuple(),
-LL | | Unit,
-LL | | }
- | |_^
-help: you might have meant to use one of the following enum variants
- |
-LL | (A::Struct {}).foo();
- | ~~~~~~~~~~~~~~
-LL | (A::Tuple()).foo();
- | ~~~~~~~~~~~~
-LL | A::Unit.foo();
- | ~~~~~~~
-help: the following enum variants are available
- |
-LL | (A::StructWithFields { /* fields */ }).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-LL | (A::TupleWithFields(/* fields */)).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error[E0423]: expected value, found enum `B`
- --> $DIR/issue-73427.rs:31:5
- |
-LL | B.foo();
- | ^
- |
-note: the enum is defined here
- --> $DIR/issue-73427.rs:9:1
- |
-LL | / enum B {
-LL | | StructWithFields { x: () },
-LL | | TupleWithFields(()),
-LL | | }
- | |_^
-help: the following enum variants are available
- |
-LL | (B::StructWithFields { /* fields */ }).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-LL | (B::TupleWithFields(/* fields */)).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error[E0423]: expected value, found enum `C`
- --> $DIR/issue-73427.rs:33:5
- |
-LL | C.foo();
- | ^
- |
-note: the enum is defined here
- --> $DIR/issue-73427.rs:14:1
- |
-LL | / enum C {
-LL | | StructWithFields { x: () },
-LL | | TupleWithFields(()),
-LL | | Unit,
-LL | | }
- | |_^
-help: you might have meant to use the following enum variant
- |
-LL | C::Unit.foo();
- | ~~~~~~~
-help: the following enum variants are available
- |
-LL | (C::StructWithFields { /* fields */ }).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-LL | (C::TupleWithFields(/* fields */)).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error[E0423]: expected value, found enum `D`
- --> $DIR/issue-73427.rs:35:5
- |
-LL | D.foo();
- | ^
- |
-note: the enum is defined here
- --> $DIR/issue-73427.rs:20:1
- |
-LL | / enum D {
-LL | | TupleWithFields(()),
-LL | | Unit,
-LL | | }
- | |_^
-help: you might have meant to use the following enum variant
- |
-LL | D::Unit.foo();
- | ~~~~~~~
-help: the following enum variant is available
- |
-LL | (D::TupleWithFields(/* fields */)).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error[E0423]: expected function, tuple struct or tuple variant, found enum `A`
- --> $DIR/issue-73427.rs:40:13
- |
-LL | let x = A(3);
- | ^
- |
- = help: you might have meant to construct one of the enum's non-tuple variants
-note: the enum is defined here
- --> $DIR/issue-73427.rs:1:1
- |
-LL | / enum A {
-LL | | StructWithFields { x: () },
-LL | | TupleWithFields(()),
-LL | | Struct {},
-LL | | Tuple(),
-LL | | Unit,
-LL | | }
- | |_^
-help: try to construct one of the enum's variants
- |
-LL | let x = A::Tuple(3);
- | ~~~~~~~~
-LL | let x = A::TupleWithFields(3);
- | ~~~~~~~~~~~~~~~~~~
-
-error[E0532]: expected tuple struct or tuple variant, found enum `A`
- --> $DIR/issue-73427.rs:42:12
- |
-LL | if let A(3) = x { }
- | ^
- |
- = help: you might have meant to match against one of the enum's non-tuple variants
-note: the enum is defined here
- --> $DIR/issue-73427.rs:1:1
- |
-LL | / enum A {
-LL | | StructWithFields { x: () },
-LL | | TupleWithFields(()),
-LL | | Struct {},
-LL | | Tuple(),
-LL | | Unit,
-LL | | }
- | |_^
-help: try to match against one of the enum's variants
- |
-LL | if let A::Tuple(3) = x { }
- | ~~~~~~~~
-LL | if let A::TupleWithFields(3) = x { }
- | ~~~~~~~~~~~~~~~~~~
-
-error: aborting due to 6 previous errors
-
-Some errors have detailed explanations: E0423, E0532.
-For more information about an error, try `rustc --explain E0423`.
--- /dev/null
+macro_rules! m { ($($t:tt)*) => { $($t)* } }
+
+fn main() {
+ m!($t); //~ ERROR expected expression
+}
--- /dev/null
+error: expected expression, found `$`
+ --> $DIR/issue-35450.rs:4:8
+ |
+LL | m!($t);
+ | ^ expected expression
+
+error: aborting due to previous error
+
|
LL | use b::bar;
|
+help: if you import `bar`, refer to it directly
+ |
+LL - a::bar();
+LL + bar();
+ |
error: aborting due to previous error
check!("0"); // OK
check!(0); // OK
check!(0u8); //~ ERROR suffixed literals are not allowed in attributes
-check!(-0); //~ ERROR unexpected token: `-0`
-check!(0 + 0); //~ ERROR unexpected token: `0 + 0`
+check!(-0); //~ ERROR unexpected expression: `-0`
+check!(0 + 0); //~ ERROR unexpected expression: `0 + 0`
fn main() {}
|
= help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
-error: unexpected token: `-0`
+error: unexpected expression: `-0`
--> $DIR/malformed-interpolated.rs:13:8
|
LL | check!(-0);
| ^^
-error: unexpected token: `0 + 0`
+error: unexpected expression: `0 + 0`
--> $DIR/malformed-interpolated.rs:14:8
|
LL | check!(0 + 0);
--- /dev/null
+trait T {
+ fn f(&self, _: ()) {
+ None::<()>.map(Self::f);
+ }
+ //~^^ ERROR function is expected to take a single 0-tuple as argument
+}
+
+fn main() {}
--- /dev/null
+error[E0593]: function is expected to take a single 0-tuple as argument, but it takes 2 distinct arguments
+ --> $DIR/issue-47706-trait.rs:3:24
+ |
+LL | fn f(&self, _: ()) {
+ | ------------------ takes 2 distinct arguments
+LL | None::<()>.map(Self::f);
+ | --- ^^^^^^^ expected function that takes a single 0-tuple as argument
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `Option::<T>::map`
+ --> $SRC_DIR/core/src/option.rs:LL:COL
+ |
+LL | F: ~const FnOnce(T) -> U,
+ | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Option::<T>::map`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0593`.
--- /dev/null
+pub struct Foo {
+ foo: Option<i32>,
+}
+
+impl Foo {
+ pub fn new(foo: Option<i32>, _: ()) -> Foo {
+ Foo { foo }
+ }
+
+ pub fn map(self) -> Option<Foo> {
+ self.foo.map(Foo::new)
+ }
+ //~^^ ERROR function is expected to take 1 argument, but it takes 2 arguments [E0593]
+}
+
+enum Qux {
+ Bar(i32),
+}
+
+fn foo<F>(f: F)
+where
+ F: Fn(),
+{
+}
+
+fn main() {
+ foo(Qux::Bar);
+}
+//~^^ ERROR function is expected to take 0 arguments, but it takes 1 argument [E0593]
--- /dev/null
+error[E0593]: function is expected to take 1 argument, but it takes 2 arguments
+ --> $DIR/issue-47706.rs:11:22
+ |
+LL | pub fn new(foo: Option<i32>, _: ()) -> Foo {
+ | ------------------------------------------ takes 2 arguments
+...
+LL | self.foo.map(Foo::new)
+ | --- ^^^^^^^^ expected function that takes 1 argument
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `Option::<T>::map`
+ --> $SRC_DIR/core/src/option.rs:LL:COL
+ |
+LL | F: ~const FnOnce(T) -> U,
+ | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Option::<T>::map`
+
+error[E0593]: function is expected to take 0 arguments, but it takes 1 argument
+ --> $DIR/issue-47706.rs:27:9
+ |
+LL | Bar(i32),
+ | -------- takes 1 argument
+...
+LL | foo(Qux::Bar);
+ | --- ^^^^^^^^ expected function that takes 0 arguments
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `foo`
+ --> $DIR/issue-47706.rs:22:8
+ |
+LL | fn foo<F>(f: F)
+ | --- required by a bound in this
+LL | where
+LL | F: Fn(),
+ | ^^^^ required by this bound in `foo`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0593`.
+++ /dev/null
-// run-pass
-
-#![allow(dead_code)]
-
-use std::mem::{size_of, align_of};
-use std::os::raw::c_int;
-
-// The two enums that follow are designed so that bugs trigger layout optimization.
-// Specifically, if either of the following reprs used here is not detected by the compiler,
-// then the sizes will be wrong.
-
-#[repr(C, u8)]
-enum E1 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-#[repr(u8, C)]
-enum E2 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-// Check that repr(int) and repr(C) are in fact different from the above
-
-#[repr(u8)]
-enum E3 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-#[repr(u16)]
-enum E4 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-#[repr(u32)]
-enum E5 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-#[repr(u64)]
-enum E6 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-#[repr(C)]
-enum E7 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-// From pr 37429
-
-#[repr(C,packed)]
-pub struct p0f_api_query {
- pub magic: u32,
- pub addr_type: u8,
- pub addr: [u8; 16],
-}
-
-pub fn main() {
- assert_eq!(size_of::<E1>(), 8);
- assert_eq!(size_of::<E2>(), 8);
- assert_eq!(size_of::<E3>(), 6);
- assert_eq!(size_of::<E4>(), 8);
- assert_eq!(size_of::<E5>(), align_size(10, align_of::<u32>()));
- assert_eq!(size_of::<E6>(), align_size(14, align_of::<u64>()));
- assert_eq!(size_of::<E7>(), align_size(6 + size_of::<c_int>(), align_of::<c_int>()));
- assert_eq!(size_of::<p0f_api_query>(), 21);
-}
-
-fn align_size(size: usize, align: usize) -> usize {
- if size % align != 0 {
- size + (align - (size % align))
- } else {
- size
- }
-}
|
LL | use xm2::S;
|
+help: if you import `S`, refer to it directly
+ |
+LL - check(m1::S);
+LL + check(S);
+ |
error[E0423]: expected value, found type alias `xm1::S`
--> $DIR/namespace-mix.rs:40:11
|
LL | use xm2::S;
|
+help: if you import `S`, refer to it directly
+ |
+LL - check(xm1::S);
+LL + check(S);
+ |
error[E0423]: expected value, found struct variant `m7::V`
--> $DIR/namespace-mix.rs:100:11
|
LL | use xm8::V;
|
+help: if you import `V`, refer to it directly
+ |
+LL - check(m7::V);
+LL + check(V);
+ |
error[E0423]: expected value, found struct variant `xm7::V`
--> $DIR/namespace-mix.rs:106:11
|
LL | use xm8::V;
|
+help: if you import `V`, refer to it directly
+ |
+LL - check(xm7::V);
+LL + check(V);
+ |
error[E0277]: the trait bound `c::Item: Impossible` is not satisfied
--> $DIR/namespace-mix.rs:33:11
--- /dev/null
+error: lifetime may not live long enough
+ --> $DIR/issue-52213.rs:3:20
+ |
+LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
+ | -- -- lifetime `'b` defined here
+ | |
+ | lifetime `'a` defined here
+LL | match (&t,) {
+LL | ((u,),) => u,
+ | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
+ |
+ = help: consider adding the following bound: `'a: 'b`
+
+error: aborting due to previous error
+
--- /dev/null
+fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
+ match (&t,) { //~ ERROR cannot infer an appropriate lifetime
+ ((u,),) => u,
+ }
+}
+
+fn main() {
+ let x = {
+ let y = Box::new((42,));
+ transmute_lifetime(&y)
+ };
+
+ println!("{}", x);
+}
--- /dev/null
+error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
+ --> $DIR/issue-52213.rs:2:11
+ |
+LL | match (&t,) {
+ | ^^^^^
+ |
+note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
+ --> $DIR/issue-52213.rs:1:23
+ |
+LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
+ | ^^
+note: ...so that the types are compatible
+ --> $DIR/issue-52213.rs:2:11
+ |
+LL | match (&t,) {
+ | ^^^^^
+ = note: expected `(&&(T,),)`
+ found `(&&'a (T,),)`
+note: but, the lifetime must be valid for the lifetime `'b` as defined here...
+ --> $DIR/issue-52213.rs:1:27
+ |
+LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
+ | ^^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/issue-52213.rs:3:20
+ |
+LL | ((u,),) => u,
+ | ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0495`.
|
LL | use hi_str;
|
+help: if you import `hi_str`, refer to it directly
+ |
+LL - println!("{}", circular_modules_main::hi_str());
+LL + println!("{}", hi_str());
+ |
error: aborting due to 2 previous errors
LL | }
| - the item list ends here
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to previous error
//~| NOTE expected one of `extern` or `fn`
//~| HELP `const` must come before `async unsafe`
//~| SUGGESTION const async unsafe
-//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
| | expected one of `extern` or `fn`
| help: `const` must come before `async unsafe`: `const async unsafe`
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to previous error
//~| NOTE expected one of `extern` or `fn`
//~| HELP `async` must come before `unsafe`
//~| SUGGESTION async unsafe
-//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
| | expected one of `extern` or `fn`
| help: `async` must come before `unsafe`: `async unsafe`
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to previous error
//~| NOTE expected one of `extern` or `fn`
//~| HELP `const` must come before `unsafe`
//~| SUGGESTION const unsafe
-//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
| | expected one of `extern` or `fn`
| help: `const` must come before `unsafe`: `const unsafe`
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to previous error
//~| NOTE expected `fn`
//~| HELP `unsafe` must come before `extern`
//~| SUGGESTION unsafe extern
-//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
| | expected `fn`
| help: `unsafe` must come before `extern`: `unsafe extern`
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to previous error
|
LL | use namespaced_enums::Foo::A;
|
+help: if you import `A`, refer to it directly
+ |
+LL - let _ = namespaced_enums::A;
+LL + let _ = A;
+ |
error[E0425]: cannot find function, tuple struct or tuple variant `B` in crate `namespaced_enums`
--> $DIR/enums-are-namespaced-xc.rs:7:31
|
LL | use namespaced_enums::Foo::B;
|
+help: if you import `B`, refer to it directly
+ |
+LL - let _ = namespaced_enums::B(10);
+LL + let _ = B(10);
+ |
error[E0422]: cannot find struct, variant or union type `C` in crate `namespaced_enums`
--> $DIR/enums-are-namespaced-xc.rs:9:31
|
LL | use namespaced_enums::Foo::C;
|
+help: if you import `C`, refer to it directly
+ |
+LL - let _ = namespaced_enums::C { a: 10 };
+LL + let _ = C { a: 10 };
+ |
error: aborting due to 3 previous errors
|
LL | use std::f64::consts::LOG10_2;
|
+help: if you import `LOG10_2`, refer to it directly
+ |
+LL - const M: usize = (f64::from(N) * std::f64::LOG10_2) as usize;
+LL + const M: usize = (f64::from(N) * LOG10_2) as usize;
+ |
error: aborting due to previous error
--- /dev/null
+enum A {
+ StructWithFields { x: () },
+ TupleWithFields(()),
+ Struct {},
+ Tuple(),
+ Unit,
+}
+
+enum B {
+ StructWithFields { x: () },
+ TupleWithFields(()),
+}
+
+enum C {
+ StructWithFields { x: () },
+ TupleWithFields(()),
+ Unit,
+}
+
+enum D {
+ TupleWithFields(()),
+ Unit,
+}
+
+fn main() {
+ // Only variants without fields are suggested (and others mentioned in a note) where an enum
+ // is used rather than a variant.
+
+ A.foo();
+ //~^ ERROR expected value, found enum `A`
+ B.foo();
+ //~^ ERROR expected value, found enum `B`
+ C.foo();
+ //~^ ERROR expected value, found enum `C`
+ D.foo();
+ //~^ ERROR expected value, found enum `D`
+
+ // Only tuple variants are suggested in calls or tuple struct pattern matching.
+
+ let x = A(3);
+ //~^ ERROR expected function, tuple struct or tuple variant, found enum `A`
+ if let A(3) = x { }
+ //~^ ERROR expected tuple struct or tuple variant, found enum `A`
+}
--- /dev/null
+error[E0423]: expected value, found enum `A`
+ --> $DIR/issue-73427.rs:29:5
+ |
+LL | A.foo();
+ | ^
+ |
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:1:1
+ |
+LL | / enum A {
+LL | | StructWithFields { x: () },
+LL | | TupleWithFields(()),
+LL | | Struct {},
+LL | | Tuple(),
+LL | | Unit,
+LL | | }
+ | |_^
+help: you might have meant to use one of the following enum variants
+ |
+LL | (A::Struct {}).foo();
+ | ~~~~~~~~~~~~~~
+LL | (A::Tuple()).foo();
+ | ~~~~~~~~~~~~
+LL | A::Unit.foo();
+ | ~~~~~~~
+help: the following enum variants are available
+ |
+LL | (A::StructWithFields { /* fields */ }).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL | (A::TupleWithFields(/* fields */)).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0423]: expected value, found enum `B`
+ --> $DIR/issue-73427.rs:31:5
+ |
+LL | B.foo();
+ | ^
+ |
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:9:1
+ |
+LL | / enum B {
+LL | | StructWithFields { x: () },
+LL | | TupleWithFields(()),
+LL | | }
+ | |_^
+help: the following enum variants are available
+ |
+LL | (B::StructWithFields { /* fields */ }).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL | (B::TupleWithFields(/* fields */)).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0423]: expected value, found enum `C`
+ --> $DIR/issue-73427.rs:33:5
+ |
+LL | C.foo();
+ | ^
+ |
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:14:1
+ |
+LL | / enum C {
+LL | | StructWithFields { x: () },
+LL | | TupleWithFields(()),
+LL | | Unit,
+LL | | }
+ | |_^
+help: you might have meant to use the following enum variant
+ |
+LL | C::Unit.foo();
+ | ~~~~~~~
+help: the following enum variants are available
+ |
+LL | (C::StructWithFields { /* fields */ }).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL | (C::TupleWithFields(/* fields */)).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0423]: expected value, found enum `D`
+ --> $DIR/issue-73427.rs:35:5
+ |
+LL | D.foo();
+ | ^
+ |
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:20:1
+ |
+LL | / enum D {
+LL | | TupleWithFields(()),
+LL | | Unit,
+LL | | }
+ | |_^
+help: you might have meant to use the following enum variant
+ |
+LL | D::Unit.foo();
+ | ~~~~~~~
+help: the following enum variant is available
+ |
+LL | (D::TupleWithFields(/* fields */)).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0423]: expected function, tuple struct or tuple variant, found enum `A`
+ --> $DIR/issue-73427.rs:40:13
+ |
+LL | let x = A(3);
+ | ^
+ |
+ = help: you might have meant to construct one of the enum's non-tuple variants
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:1:1
+ |
+LL | / enum A {
+LL | | StructWithFields { x: () },
+LL | | TupleWithFields(()),
+LL | | Struct {},
+LL | | Tuple(),
+LL | | Unit,
+LL | | }
+ | |_^
+help: try to construct one of the enum's variants
+ |
+LL | let x = A::Tuple(3);
+ | ~~~~~~~~
+LL | let x = A::TupleWithFields(3);
+ | ~~~~~~~~~~~~~~~~~~
+
+error[E0532]: expected tuple struct or tuple variant, found enum `A`
+ --> $DIR/issue-73427.rs:42:12
+ |
+LL | if let A(3) = x { }
+ | ^
+ |
+ = help: you might have meant to match against one of the enum's non-tuple variants
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:1:1
+ |
+LL | / enum A {
+LL | | StructWithFields { x: () },
+LL | | TupleWithFields(()),
+LL | | Struct {},
+LL | | Tuple(),
+LL | | Unit,
+LL | | }
+ | |_^
+help: try to match against one of the enum's variants
+ |
+LL | if let A::Tuple(3) = x { }
+ | ~~~~~~~~
+LL | if let A::TupleWithFields(3) = x { }
+ | ~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0423, E0532.
+For more information about an error, try `rustc --explain E0423`.
|
LL | use std::collections::HashMap;
|
+help: if you import `HashMap`, refer to it directly
+ |
+LL - let _map = std::hahmap::HashMap::new();
+LL + let _map = HashMap::new();
+ |
error: aborting due to previous error
|
LL | use std::f64::consts::E;
|
+help: if you import `E`, refer to it directly
+ |
+LL - let _: E = m::E;
+LL + let _: E = E;
+ |
error[E0423]: expected value, found struct variant `m::E::Struct`
--> $DIR/privacy-enum-ctor.rs:45:16
|
LL | use std::primitive::u8;
|
+help: if you import `u8`, refer to it directly
+ |
+LL - let _: ::u8;
+LL + let _: u8;
+ |
error[E0061]: this function takes 0 arguments but 1 argument was supplied
--> $DIR/resolve-primitive-fallback.rs:3:5
--- /dev/null
+// check-pass
+// compile-flags: -Zsave-analysis
+
+#![feature(rustc_attrs)]
+#![allow(warnings)]
+
+#[derive(Debug)]
+struct Point {
+}
+
+struct NestedA<'a, 'b> {
+ x: &'a NestedB<'b>
+}
+
+struct NestedB<'a> {
+ x: &'a i32,
+}
+
+fn main() {
+}
--- /dev/null
+// run-pass
+
+#![allow(dead_code)]
+
+use std::mem::{size_of, align_of};
+use std::os::raw::c_int;
+
+// The two enums that follow are designed so that bugs trigger layout optimization.
+// Specifically, if either of the following reprs used here is not detected by the compiler,
+// then the sizes will be wrong.
+
+#[repr(C, u8)]
+enum E1 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+#[repr(u8, C)]
+enum E2 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+// Check that repr(int) and repr(C) are in fact different from the above
+
+#[repr(u8)]
+enum E3 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+#[repr(u16)]
+enum E4 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+#[repr(u32)]
+enum E5 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+#[repr(u64)]
+enum E6 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+#[repr(C)]
+enum E7 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+// From pr 37429
+
+#[repr(C,packed)]
+pub struct p0f_api_query {
+ pub magic: u32,
+ pub addr_type: u8,
+ pub addr: [u8; 16],
+}
+
+pub fn main() {
+ assert_eq!(size_of::<E1>(), 8);
+ assert_eq!(size_of::<E2>(), 8);
+ assert_eq!(size_of::<E3>(), 6);
+ assert_eq!(size_of::<E4>(), 8);
+ assert_eq!(size_of::<E5>(), align_size(10, align_of::<u32>()));
+ assert_eq!(size_of::<E6>(), align_size(14, align_of::<u64>()));
+ assert_eq!(size_of::<E7>(), align_size(6 + size_of::<c_int>(), align_of::<c_int>()));
+ assert_eq!(size_of::<p0f_api_query>(), 21);
+}
+
+fn align_size(size: usize, align: usize) -> usize {
+ if size % align != 0 {
+ size + (align - (size % align))
+ } else {
+ size
+ }
+}
--- /dev/null
+// Previously ICEd because we didn't properly track binders in suggestions
+// check-fail
+
+pub trait Foo<'de>: Sized {}
+
+pub trait Bar<'a>: 'static {
+ type Inner: 'a;
+}
+
+pub trait Fubar {
+ type Bar: for<'a> Bar<'a>;
+}
+
+pub struct Baz<T>(pub T);
+
+impl<'de, T> Foo<'de> for Baz<T> where T: Foo<'de> {}
+
+struct Empty;
+
+impl<M> Dummy<M> for Empty
+where
+ M: Fubar,
+ for<'de> Baz<<M::Bar as Bar<'de>>::Inner>: Foo<'de>,
+{
+}
+
+pub trait Dummy<M>
+where
+ M: Fubar,
+{
+}
+
+pub struct EmptyBis<'a>(&'a [u8]);
+
+impl<'a> Bar<'a> for EmptyBis<'static> {
+ type Inner = EmptyBis<'a>;
+}
+
+pub struct EmptyMarker;
+
+impl Fubar for EmptyMarker {
+ type Bar = EmptyBis<'static>;
+}
+
+fn icey_bounds<D: Dummy<EmptyMarker>>(p: &D) {}
+
+fn trigger_ice() {
+ let p = Empty;
+ icey_bounds(&p); //~ERROR the trait bound
+}
+
+fn main() {}
--- /dev/null
+error[E0277]: the trait bound `for<'de> EmptyBis<'de>: Foo<'_>` is not satisfied
+ --> $DIR/issue-96223.rs:49:17
+ |
+LL | icey_bounds(&p);
+ | ----------- ^^ the trait `for<'de> Foo<'_>` is not implemented for `EmptyBis<'de>`
+ | |
+ | required by a bound introduced by this call
+ |
+ = help: the trait `Foo<'de>` is implemented for `Baz<T>`
+note: required because of the requirements on the impl of `for<'de> Foo<'de>` for `Baz<EmptyBis<'de>>`
+ --> $DIR/issue-96223.rs:16:14
+ |
+LL | impl<'de, T> Foo<'de> for Baz<T> where T: Foo<'de> {}
+ | ^^^^^^^^ ^^^^^^
+note: required because of the requirements on the impl of `Dummy<EmptyMarker>` for `Empty`
+ --> $DIR/issue-96223.rs:20:9
+ |
+LL | impl<M> Dummy<M> for Empty
+ | ^^^^^^^^ ^^^^^
+note: required by a bound in `icey_bounds`
+ --> $DIR/issue-96223.rs:45:19
+ |
+LL | fn icey_bounds<D: Dummy<EmptyMarker>>(p: &D) {}
+ | ^^^^^^^^^^^^^^^^^^ required by this bound in `icey_bounds`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// run-rustfix
+#![allow(non_snake_case)]
+mod A {
+ pub trait Trait {}
+ impl Trait for i32 {}
+}
+
+mod B {
+ use A::Trait;
+
+pub struct A<H: Trait>(pub H); //~ ERROR cannot find trait
+}
+
+fn main() {
+ let _ = B::A(42);
+}
--- /dev/null
+// run-rustfix
+#![allow(non_snake_case)]
+mod A {
+ pub trait Trait {}
+ impl Trait for i32 {}
+}
+
+mod B {
+ pub struct A<H: A::Trait>(pub H); //~ ERROR cannot find trait
+}
+
+fn main() {
+ let _ = B::A(42);
+}
--- /dev/null
+error[E0405]: cannot find trait `Trait` in `A`
+ --> $DIR/shadowed-path-in-trait-bound-suggestion.rs:9:24
+ |
+LL | pub struct A<H: A::Trait>(pub H);
+ | ^^^^^ not found in `A`
+ |
+help: consider importing this trait
+ |
+LL | use A::Trait;
+ |
+help: if you import `Trait`, refer to it directly
+ |
+LL - pub struct A<H: A::Trait>(pub H);
+LL + pub struct A<H: Trait>(pub H);
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0405`.
--- /dev/null
+use std::mem;
+
+trait Misc {}
+
+fn size_of_copy<T: Copy+?Sized>() -> usize { mem::size_of::<T>() }
+
+fn main() {
+ size_of_copy::<dyn Misc + Copy>();
+ //~^ ERROR only auto traits can be used as additional traits in a trait object
+ //~| ERROR only auto traits can be used as additional traits in a trait object
+ //~| ERROR the trait bound `dyn Misc: Copy` is not satisfied
+}
--- /dev/null
+error[E0225]: only auto traits can be used as additional traits in a trait object
+ --> $DIR/issue-32963.rs:8:31
+ |
+LL | size_of_copy::<dyn Misc + Copy>();
+ | ---- ^^^^ additional non-auto trait
+ | |
+ | first non-auto trait
+ |
+ = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Misc + Copy {}`
+ = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
+
+error[E0225]: only auto traits can be used as additional traits in a trait object
+ --> $DIR/issue-32963.rs:8:31
+ |
+LL | size_of_copy::<dyn Misc + Copy>();
+ | ---- ^^^^ additional non-auto trait
+ | |
+ | first non-auto trait
+ |
+ = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Misc + Copy {}`
+ = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
+
+error[E0277]: the trait bound `dyn Misc: Copy` is not satisfied
+ --> $DIR/issue-32963.rs:8:5
+ |
+LL | size_of_copy::<dyn Misc + Copy>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `dyn Misc`
+ |
+note: required by a bound in `size_of_copy`
+ --> $DIR/issue-32963.rs:5:20
+ |
+LL | fn size_of_copy<T: Copy+?Sized>() -> usize { mem::size_of::<T>() }
+ | ^^^^ required by this bound in `size_of_copy`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0225, E0277.
+For more information about an error, try `rustc --explain E0225`.
--- /dev/null
+// run-pass
+#![allow(dead_code)]
+
+pub fn main() {
+ let _id: &Mat2<f64> = &Matrix::identity(1.0);
+}
+
+pub trait Index<Index,Result> { fn get(&self, _: Index) -> Result { panic!() } }
+pub trait Dimensional<T>: Index<usize, T> { }
+
+pub struct Mat2<T> { x: T }
+pub struct Vec2<T> { x: T }
+
+impl<T> Dimensional<Vec2<T>> for Mat2<T> { }
+impl<T> Index<usize, Vec2<T>> for Mat2<T> { }
+
+impl<T> Dimensional<T> for Vec2<T> { }
+impl<T> Index<usize, T> for Vec2<T> { }
+
+pub trait Matrix<T,V>: Dimensional<V> {
+ fn identity(t:T) -> Self;
+}
+
+impl<T> Matrix<T, Vec2<T>> for Mat2<T> {
+ fn identity(t:T) -> Mat2<T> { Mat2{ x: t } }
+}
| |_- this function returns a `Result`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u64, String>`
- = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Result<T, F> as FromResidual<Result<Infallible, E>>>
+ <Result<T, F> as FromResidual<Yeet<E>>>
error[E0277]: the `?` operator can only be used on `Result`s in a function that returns `Result`
--> $DIR/bad-interconversion.rs:17:31
| |_- this function returns a `Result`
|
= help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Result<u64, String>`
- = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Result<T, F> as FromResidual<Result<Infallible, E>>>
+ <Result<T, F> as FromResidual<Yeet<E>>>
error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
--> $DIR/bad-interconversion.rs:22:22
| |_- this function returns an `Option`
|
= help: the trait `FromResidual<Result<Infallible, &str>>` is not implemented for `Option<u16>`
- = help: the trait `FromResidual` is implemented for `Option<T>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Option<T> as FromResidual<Yeet<()>>>
+ <Option<T> as FromResidual>
error[E0277]: the `?` operator can only be used on `Option`s in a function that returns `Option`
--> $DIR/bad-interconversion.rs:27:33
| |_- this function returns an `Option`
|
= help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Option<u64>`
- = help: the trait `FromResidual` is implemented for `Option<T>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Option<T> as FromResidual<Yeet<()>>>
+ <Option<T> as FromResidual>
error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow`
--> $DIR/bad-interconversion.rs:32:39
| |_- this function returns a `Result`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<(), ()>`
- = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Result<T, F> as FromResidual<Result<Infallible, E>>>
+ <Result<T, F> as FromResidual<Yeet<E>>>
error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
--> $DIR/option-to-result.rs:11:6
| |_- this function returns an `Option`
|
= help: the trait `FromResidual<Result<Infallible, i32>>` is not implemented for `Option<i32>`
- = help: the trait `FromResidual` is implemented for `Option<T>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Option<T> as FromResidual<Yeet<()>>>
+ <Option<T> as FromResidual>
error: aborting due to 2 previous errors
| |_- this function returns a `Result`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u32, ()>`
- = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Result<T, F> as FromResidual<Result<Infallible, E>>>
+ <Result<T, F> as FromResidual<Yeet<E>>>
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> $DIR/try-on-option.rs:11:6
--- /dev/null
+// run-pass
+
+#![feature(yeet_expr)]
+
+fn always_yeet() -> Option<String> {
+ do yeet;
+}
+
+fn main() {
+ assert_eq!(always_yeet(), None);
+}
--- /dev/null
+// run-pass
+
+#![feature(yeet_expr)]
+
+fn always_yeet() -> Result<i32, String> {
+ do yeet "hello";
+}
+
+fn main() {
+ assert_eq!(always_yeet(), Err("hello".to_string()));
+}
--- /dev/null
+#![feature(type_alias_impl_trait)]
+
+type Foo = impl Fn() -> Foo;
+
+fn foo() -> Foo {
+ foo //~ ERROR: overflow evaluating the requirement `fn() -> Foo {foo}: Sized`
+}
+
+fn main() {}
--- /dev/null
+error[E0275]: overflow evaluating the requirement `fn() -> Foo {foo}: Sized`
+ --> $DIR/issue-53398-cyclic-types.rs:6:5
+ |
+LL | foo
+ | ^^^
+ |
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_53398_cyclic_types`)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
--- /dev/null
+// check-pass
+
+#![feature(generators, generator_trait)]
+#![feature(type_alias_impl_trait)]
+
+use std::ops::{Generator, GeneratorState};
+use std::pin::Pin;
+
+type RandGenerator<'a> = impl Generator<Return = (), Yield = u64> + 'a;
+fn rand_generator<'a>(rng: &'a ()) -> RandGenerator<'a> {
+ move || {
+ let _rng = rng;
+ loop {
+ yield 0;
+ }
+ }
+}
+
+pub type RandGeneratorWithIndirection<'a> = impl Generator<Return = (), Yield = u64> + 'a;
+pub fn rand_generator_with_indirection<'a>(rng: &'a ()) -> RandGeneratorWithIndirection<'a> {
+ fn helper<'b>(rng: &'b ()) -> impl 'b + Generator<Return = (), Yield = u64> {
+ move || {
+ let _rng = rng;
+ loop {
+ yield 0;
+ }
+ }
+ }
+
+ helper(rng)
+}
+
+fn main() {
+ let mut gen = rand_generator(&());
+ match unsafe { Pin::new_unchecked(&mut gen) }.resume(()) {
+ GeneratorState::Yielded(_) => {}
+ GeneratorState::Complete(_) => {}
+ };
+}
--- /dev/null
+// check-pass
+
+#![feature(type_alias_impl_trait)]
+
+trait SomeTrait {}
+impl SomeTrait for () {}
+
+trait MyFuture {
+ type Output;
+}
+impl<T> MyFuture for T {
+ type Output = T;
+}
+
+trait ReturnsFuture {
+ type Output: SomeTrait;
+ type Future: MyFuture<Output = Result<Self::Output, ()>>;
+ fn func() -> Self::Future;
+}
+
+struct Foo;
+
+impl ReturnsFuture for Foo {
+ type Output = impl SomeTrait;
+ type Future = impl MyFuture<Output = Result<Self::Output, ()>>;
+ fn func() -> Self::Future {
+ Result::<(), ()>::Err(())
+ }
+}
+
+fn main() {}
--- /dev/null
+#![feature(type_alias_impl_trait, generator_trait, generators)]
+use std::ops::Generator;
+
+trait Runnable {
+ type Gen: Generator<Yield = (), Return = ()>;
+
+ fn run(&mut self) -> Self::Gen;
+}
+
+struct Implementor {}
+
+impl Runnable for Implementor {
+ type Gen = impl Generator<Yield = (), Return = ()>;
+
+ fn run(&mut self) -> Self::Gen {
+ move || { //~ ERROR: type mismatch resolving
+ yield 1;
+ }
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0271]: type mismatch resolving `<[generator@$DIR/issue-94429.rs:16:9: 18:10] as Generator>::Yield == ()`
+ --> $DIR/issue-94429.rs:16:9
+ |
+LL | / move || {
+LL | | yield 1;
+LL | | }
+ | |_________^ expected integer, found `()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0271`.
--- /dev/null
+// run-rustfix
+// Check that the HELP suggestion is `l(vec![])` instead of `l($crate::vec::Vec::new())`
+fn l(_a: Vec<u8>) {}
+
+fn main() {
+ l(vec![])
+ //~^ ERROR this function takes 1 argument but 2 arguments were supplied
+ //~| HELP remove the extra argument
+}
--- /dev/null
+// run-rustfix
+// Check that the HELP suggestion is `l(vec![])` instead of `l($crate::vec::Vec::new())`
+fn l(_a: Vec<u8>) {}
+
+fn main() {
+ l(vec![], vec![])
+ //~^ ERROR this function takes 1 argument but 2 arguments were supplied
+ //~| HELP remove the extra argument
+}
--- /dev/null
+error[E0061]: this function takes 1 argument but 2 arguments were supplied
+ --> $DIR/remove-extra-argument.rs:6:5
+ |
+LL | l(vec![], vec![])
+ | ^ ------ argument unexpected
+ |
+note: function defined here
+ --> $DIR/remove-extra-argument.rs:3:4
+ |
+LL | fn l(_a: Vec<u8>) {}
+ | ^ -----------
+help: remove the extra argument
+ |
+LL | l(vec![])
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0061`.
assert_eq!(val, ());
}
+fn bathroom_stall() {
+ let mut i = 1;
+ matches!(2, _|_|_|_|_|_ if (i+=1) != (i+=1));
+ assert_eq!(i, 13);
+}
+
pub fn main() {
strange();
funny();
i_yield();
match_nested_if();
monkey_barrel();
+ bathroom_stall();
}
-Subproject commit f63f23ff1f1a12ede8585bbd1bbf0c536e50293d
+Subproject commit a44758ac805600edbb6ba51e7e6fb81a6077c0cd
PatKind::TupleStruct(ref qpath, [sub_pat], _) => {
if let PatKind::Wild = sub_pat.kind {
let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id);
- let Some(id) = res.opt_def_id().and_then(|ctor_id| cx.tcx.parent(ctor_id)) else { return };
+ let Some(id) = res.opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) else { return };
let lang_items = cx.tcx.lang_items();
if Some(id) == lang_items.result_ok_variant() {
("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
fn no_op_msg(cx: &LateContext<'_>) -> Option<String> {
let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?;
- let item_id = cx.tcx.parent(variant_id)?;
+ let item_id = cx.tcx.parent(variant_id);
Some(format!(
"using `{}.{}({})`, which is a no-op",
cx.tcx.item_name(item_id),
fn lint_msg(cx: &LateContext<'_>) -> Option<String> {
let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?;
- let item_id = cx.tcx.parent(variant_id)?;
+ let item_id = cx.tcx.parent(variant_id);
Some(format!(
"using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`",
cx.tcx.item_name(item_id),
if_chain! {
if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def();
if let Ok(vid) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM);
- if Some(adt.did()) == cx.tcx.parent(vid);
+ if adt.did() == cx.tcx.parent(vid);
then {} else { return false; }
}
fn is_variant(cx: &LateContext<'_>, res: Res) -> bool {
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
if let Ok(variant_id) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM) {
- return cx.tcx.parent(id) == Some(variant_id);
+ return cx.tcx.parent(id) == variant_id;
}
}
false
if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods);
if let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind;
- if let Some(id) = path_def_id(cx, fun).and_then(|ctor_id| cx.tcx.parent(ctor_id));
+ if let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id));
if Some(id) == cx.tcx.lang_items().option_some_variant();
then {
let mut applicability = Applicability::MachineApplicable;
let arg_snippet = snippet(cx, span, "..");
let body = cx.tcx.hir().body(id);
if let Some((func, [arg_char])) = reduce_unit_expression(&body.value);
- if let Some(id) = path_def_id(cx, func).and_then(|ctor_id| cx.tcx.parent(ctor_id));
+ if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id));
if Some(id) == cx.tcx.lang_items().option_some_variant();
then {
let func_snippet = snippet(cx, arg_char.span, "..");
hir::ItemKind::Fn(..) => {
// ignore main()
if it.ident.name == sym::main {
- let at_root = cx.tcx.local_parent(it.def_id) == Some(CRATE_DEF_ID);
+ let at_root = cx.tcx.local_parent(it.def_id) == CRATE_DEF_ID;
if at_root {
return;
}
use rustc_parse::parser;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{kw, Symbol};
-use rustc_span::{sym, BytePos, Span, DUMMY_SP};
+use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP};
declare_clippy_lint! {
/// ### What it does
}
},
ArgumentNamed(n, _) => {
+ let n = Symbol::intern(n);
if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
match x.1.as_slice() {
// A non-empty format string has been seen already.
let span = parser
.arg_places
.last()
- .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(x));
+ .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(InnerSpan::new(x.start, x.end)));
if !self.in_debug_impl && arg.format.ty == "?" {
// FIXME: modify rustc's fmt string parser to give us the current span
match (l, r) {
(Empty, Empty) => true,
(Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts),
- (Eq(_, lt), Eq(_, rt)) => lt.kind == rt.kind,
+ (Eq(_, MacArgsEq::Ast(le)), Eq(_, MacArgsEq::Ast(re))) => eq_expr(le, re),
+ (Eq(_, MacArgsEq::Hir(ll)), Eq(_, MacArgsEq::Hir(rl))) => ll.kind == rl.kind,
_ => false,
}
}
if let QPath::Resolved(_, path) = qpath {
if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
- return cx.tcx.parent(ctor_id) == Some(item_id);
+ return cx.tcx.parent(ctor_id) == item_id;
}
}
}
| ast::ExprKind::Path(..)
| ast::ExprKind::Repeat(..)
| ast::ExprKind::Ret(..)
+ | ast::ExprKind::Yeet(..)
| ast::ExprKind::Struct(..)
| ast::ExprKind::Try(..)
| ast::ExprKind::TryBlock(..)
output_base_dir(config, testpaths, revision).join("stamp")
}
+fn files_related_to_test(
+ config: &Config,
+ testpaths: &TestPaths,
+ props: &EarlyProps,
+ revision: Option<&str>,
+) -> Vec<PathBuf> {
+ let mut related = vec![];
+
+ if testpaths.file.is_dir() {
+ // run-make tests use their individual directory
+ for entry in WalkDir::new(&testpaths.file) {
+ let path = entry.unwrap().into_path();
+ if path.is_file() {
+ related.push(path);
+ }
+ }
+ } else {
+ related.push(testpaths.file.clone());
+ }
+
+ for aux in &props.aux {
+ let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
+ related.push(path);
+ }
+
+ // UI test files.
+ for extension in UI_EXTENSIONS {
+ let path = expected_output_path(testpaths, revision, &config.compare_mode, extension);
+ related.push(path);
+ }
+
+ related
+}
+
fn is_up_to_date(
config: &Config,
testpaths: &TestPaths,
// Check timestamps.
let mut inputs = inputs.clone();
- // Use `add_dir` to account for run-make tests, which use their individual directory
- inputs.add_dir(&testpaths.file);
-
- for aux in &props.aux {
- let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
+ for path in files_related_to_test(config, testpaths, props, revision) {
inputs.add_path(&path);
}
- // UI test files.
- for extension in UI_EXTENSIONS {
- let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
- inputs.add_path(path);
- }
-
inputs < Stamp::from_path(&stamp_name)
}
-Subproject commit 24cf957627d5ede1b395f92ff871fd7a281d49a4
+Subproject commit 5dce1ff0212e467271c9e895478670c74d847ee9
try {
// This is more convenient that setting fields one by one.
let args = [
- "--no-screenshot-comparison",
"--variable", "DOC_PATH", opts["doc_folder"],
];
if (opts["debug"]) {
ast::ExprKind::Ret(Some(ref expr)) => {
rewrite_unary_prefix(context, "return ", &**expr, shape)
}
+ ast::ExprKind::Yeet(None) => Some("do yeet".to_owned()),
+ ast::ExprKind::Yeet(Some(ref expr)) => {
+ rewrite_unary_prefix(context, "do yeet ", &**expr, shape)
+ }
ast::ExprKind::Box(ref expr) => rewrite_unary_prefix(context, "box ", &**expr, shape),
ast::ExprKind::AddrOf(borrow_kind, mutability, ref expr) => {
rewrite_expr_addrof(context, borrow_kind, mutability, expr, shape)
| ast::ExprKind::Range(..)
| ast::ExprKind::Repeat(..)
| ast::ExprKind::Ret(..)
+ | ast::ExprKind::Yeet(..)
| ast::ExprKind::Tup(..)
| ast::ExprKind::Type(..)
| ast::ExprKind::Yield(None)