mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-22 03:44:24 +00:00
Auto merge of #133760 - GuillaumeGomez:rollup-2c1y8c3, r=GuillaumeGomez
Rollup of 13 pull requests Successful merges: - #133603 (Eliminate magic numbers from expression precedence) - #133715 (rustdoc-json: Include safety of `static`s) - #133721 (rustdoc-json: Add test for `impl Trait for dyn Trait`) - #133725 (Remove `//@ compare-output-lines-by-subset`) - #133730 (Add pretty-printer parenthesis insertion test) - #133736 (Add `needs-target-has-atomic` directive) - #133739 (Re-add myself to rotation) - #133743 (Fix docs for `<[T]>::as_array`.) - #133744 (Fix typo README.md) - #133745 (Remove static HashSet for default IDs list) - #133749 (mir validator: don't store mir phase) - #133751 (remove `Ty::is_copy_modulo_regions`) - #133757 (`impl Default for EarlyDiagCtxt`) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
d49be02cf6
@ -39,9 +39,7 @@ pub use crate::format::*;
|
||||
use crate::ptr::P;
|
||||
use crate::token::{self, CommentKind, Delimiter};
|
||||
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
|
||||
use crate::util::parser::{
|
||||
AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_RANGE, PREC_UNAMBIGUOUS,
|
||||
};
|
||||
use crate::util::parser::{AssocOp, ExprPrecedence};
|
||||
|
||||
/// A "Label" is an identifier of some point in sources,
|
||||
/// e.g. in the following code:
|
||||
@ -1317,29 +1315,29 @@ impl Expr {
|
||||
Some(P(Ty { kind, id: self.id, span: self.span, tokens: None }))
|
||||
}
|
||||
|
||||
pub fn precedence(&self) -> i8 {
|
||||
pub fn precedence(&self) -> ExprPrecedence {
|
||||
match self.kind {
|
||||
ExprKind::Closure(..) => PREC_CLOSURE,
|
||||
ExprKind::Closure(..) => ExprPrecedence::Closure,
|
||||
|
||||
ExprKind::Break(..)
|
||||
| ExprKind::Continue(..)
|
||||
| ExprKind::Ret(..)
|
||||
| ExprKind::Yield(..)
|
||||
| ExprKind::Yeet(..)
|
||||
| ExprKind::Become(..) => PREC_JUMP,
|
||||
| ExprKind::Become(..) => ExprPrecedence::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
|
||||
// ensures that `pprust` will add parentheses in the right places to get the desired
|
||||
// parse.
|
||||
ExprKind::Range(..) => PREC_RANGE,
|
||||
ExprKind::Range(..) => ExprPrecedence::Range,
|
||||
|
||||
// Binop-like expr kinds, handled by `AssocOp`.
|
||||
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8,
|
||||
ExprKind::Cast(..) => AssocOp::As.precedence() as i8,
|
||||
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
|
||||
ExprKind::Cast(..) => ExprPrecedence::Cast,
|
||||
|
||||
ExprKind::Assign(..) |
|
||||
ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8,
|
||||
ExprKind::AssignOp(..) => ExprPrecedence::Assign,
|
||||
|
||||
// Unary, prefix
|
||||
ExprKind::AddrOf(..)
|
||||
@ -1348,7 +1346,7 @@ impl Expr {
|
||||
// need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
|
||||
// but we need to print `(let _ = a) < b` as-is with parens.
|
||||
| ExprKind::Let(..)
|
||||
| ExprKind::Unary(..) => PREC_PREFIX,
|
||||
| ExprKind::Unary(..) => ExprPrecedence::Prefix,
|
||||
|
||||
// Never need parens
|
||||
ExprKind::Array(_)
|
||||
@ -1381,7 +1379,7 @@ impl Expr {
|
||||
| ExprKind::Underscore
|
||||
| ExprKind::While(..)
|
||||
| ExprKind::Err(_)
|
||||
| ExprKind::Dummy => PREC_UNAMBIGUOUS,
|
||||
| ExprKind::Dummy => ExprPrecedence::Unambiguous,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,21 +128,21 @@ impl AssocOp {
|
||||
}
|
||||
|
||||
/// Gets the precedence of this operator
|
||||
pub fn precedence(&self) -> usize {
|
||||
pub fn precedence(&self) -> ExprPrecedence {
|
||||
use AssocOp::*;
|
||||
match *self {
|
||||
As => 14,
|
||||
Multiply | Divide | Modulus => 13,
|
||||
Add | Subtract => 12,
|
||||
ShiftLeft | ShiftRight => 11,
|
||||
BitAnd => 10,
|
||||
BitXor => 9,
|
||||
BitOr => 8,
|
||||
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
|
||||
LAnd => 6,
|
||||
LOr => 5,
|
||||
DotDot | DotDotEq => 4,
|
||||
Assign | AssignOp(_) => 2,
|
||||
As => ExprPrecedence::Cast,
|
||||
Multiply | Divide | Modulus => ExprPrecedence::Product,
|
||||
Add | Subtract => ExprPrecedence::Sum,
|
||||
ShiftLeft | ShiftRight => ExprPrecedence::Shift,
|
||||
BitAnd => ExprPrecedence::BitAnd,
|
||||
BitXor => ExprPrecedence::BitXor,
|
||||
BitOr => ExprPrecedence::BitOr,
|
||||
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare,
|
||||
LAnd => ExprPrecedence::LAnd,
|
||||
LOr => ExprPrecedence::LOr,
|
||||
DotDot | DotDotEq => ExprPrecedence::Range,
|
||||
Assign | AssignOp(_) => ExprPrecedence::Assign,
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,17 +229,44 @@ impl AssocOp {
|
||||
}
|
||||
}
|
||||
|
||||
pub const PREC_CLOSURE: i8 = -40;
|
||||
pub const PREC_JUMP: i8 = -30;
|
||||
pub const PREC_RANGE: i8 = -10;
|
||||
// The range 2..=14 is reserved for AssocOp binary operator precedences.
|
||||
pub const PREC_PREFIX: i8 = 50;
|
||||
pub const PREC_UNAMBIGUOUS: i8 = 60;
|
||||
pub const PREC_FORCE_PAREN: i8 = 100;
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub enum ExprPrecedence {
|
||||
Closure,
|
||||
// return, break, yield
|
||||
Jump,
|
||||
// = += -= *= /= %= &= |= ^= <<= >>=
|
||||
Assign,
|
||||
// .. ..=
|
||||
Range,
|
||||
// ||
|
||||
LOr,
|
||||
// &&
|
||||
LAnd,
|
||||
// == != < > <= >=
|
||||
Compare,
|
||||
// |
|
||||
BitOr,
|
||||
// ^
|
||||
BitXor,
|
||||
// &
|
||||
BitAnd,
|
||||
// << >>
|
||||
Shift,
|
||||
// + -
|
||||
Sum,
|
||||
// * / %
|
||||
Product,
|
||||
// as
|
||||
Cast,
|
||||
// unary - * ! & &mut
|
||||
Prefix,
|
||||
// paths, loops, function calls, array indexing, field expressions, method calls
|
||||
Unambiguous,
|
||||
}
|
||||
|
||||
/// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`.
|
||||
pub fn prec_let_scrutinee_needs_par() -> usize {
|
||||
AssocOp::LAnd.precedence()
|
||||
pub fn prec_let_scrutinee_needs_par() -> ExprPrecedence {
|
||||
ExprPrecedence::LAnd
|
||||
}
|
||||
|
||||
/// Suppose we have `let _ = e` and the `order` of `e`.
|
||||
@ -247,8 +274,8 @@ pub fn prec_let_scrutinee_needs_par() -> usize {
|
||||
///
|
||||
/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
|
||||
/// Can we print this as `let _ = a OP b`?
|
||||
pub fn needs_par_as_let_scrutinee(order: i8) -> bool {
|
||||
order <= prec_let_scrutinee_needs_par() as i8
|
||||
pub fn needs_par_as_let_scrutinee(order: ExprPrecedence) -> bool {
|
||||
order <= prec_let_scrutinee_needs_par()
|
||||
}
|
||||
|
||||
/// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any
|
||||
|
@ -5,7 +5,7 @@ use itertools::{Itertools, Position};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::util::literal::escape_byte_str_symbol;
|
||||
use rustc_ast::util::parser::{self, AssocOp, Fixity};
|
||||
use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity};
|
||||
use rustc_ast::{
|
||||
self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
|
||||
FormatDebugHex, FormatSign, FormatTrait, token,
|
||||
@ -212,9 +212,9 @@ impl<'a> State<'a> {
|
||||
}
|
||||
|
||||
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
|
||||
let prec = match func.kind {
|
||||
ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
|
||||
_ => parser::PREC_UNAMBIGUOUS,
|
||||
let needs_paren = match func.kind {
|
||||
ast::ExprKind::Field(..) => true,
|
||||
_ => func.precedence() < ExprPrecedence::Unambiguous,
|
||||
};
|
||||
|
||||
// Independent of parenthesization related to precedence, we must
|
||||
@ -233,7 +233,7 @@ impl<'a> State<'a> {
|
||||
// because the latter is valid syntax but with the incorrect meaning.
|
||||
// It's a match-expression followed by tuple-expression, not a function
|
||||
// call.
|
||||
self.print_expr_cond_paren(func, func.precedence() < prec, fixup.leftmost_subexpression());
|
||||
self.print_expr_cond_paren(func, needs_paren, fixup.leftmost_subexpression());
|
||||
|
||||
self.print_call_post(args)
|
||||
}
|
||||
@ -256,7 +256,7 @@ impl<'a> State<'a> {
|
||||
// a statement containing an expression.
|
||||
self.print_expr_cond_paren(
|
||||
receiver,
|
||||
receiver.precedence() < parser::PREC_UNAMBIGUOUS,
|
||||
receiver.precedence() < ExprPrecedence::Unambiguous,
|
||||
fixup,
|
||||
);
|
||||
|
||||
@ -276,21 +276,22 @@ impl<'a> State<'a> {
|
||||
fixup: FixupContext,
|
||||
) {
|
||||
let assoc_op = AssocOp::from_ast_binop(op.node);
|
||||
let prec = assoc_op.precedence() as i8;
|
||||
let fixity = assoc_op.fixity();
|
||||
let binop_prec = assoc_op.precedence();
|
||||
let left_prec = lhs.precedence();
|
||||
let right_prec = rhs.precedence();
|
||||
|
||||
let (left_prec, right_prec) = match fixity {
|
||||
Fixity::Left => (prec, prec + 1),
|
||||
Fixity::Right => (prec + 1, prec),
|
||||
Fixity::None => (prec + 1, prec + 1),
|
||||
let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() {
|
||||
Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
|
||||
Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
|
||||
Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
|
||||
};
|
||||
|
||||
let left_prec = match (&lhs.kind, op.node) {
|
||||
match (&lhs.kind, op.node) {
|
||||
// These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
|
||||
// the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
|
||||
// of `(x as i32) < ...`. We need to convince it _not_ to do that.
|
||||
(&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => {
|
||||
parser::PREC_FORCE_PAREN
|
||||
left_needs_paren = true;
|
||||
}
|
||||
// We are given `(let _ = a) OP b`.
|
||||
//
|
||||
@ -300,33 +301,23 @@ impl<'a> State<'a> {
|
||||
// - Otherwise, e.g. when we have `(let a = b) < c` in AST,
|
||||
// parens are required since the parser would interpret `let a = b < c` as
|
||||
// `let a = (b < c)`. To achieve this, we force parens.
|
||||
(&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
|
||||
parser::PREC_FORCE_PAREN
|
||||
(&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(binop_prec) => {
|
||||
left_needs_paren = true;
|
||||
}
|
||||
_ => left_prec,
|
||||
};
|
||||
|
||||
self.print_expr_cond_paren(
|
||||
lhs,
|
||||
lhs.precedence() < left_prec,
|
||||
fixup.leftmost_subexpression(),
|
||||
);
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression());
|
||||
self.space();
|
||||
self.word_space(op.node.as_str());
|
||||
|
||||
self.print_expr_cond_paren(
|
||||
rhs,
|
||||
rhs.precedence() < right_prec,
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression());
|
||||
}
|
||||
|
||||
fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) {
|
||||
self.word(op.as_str());
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < parser::PREC_PREFIX,
|
||||
expr.precedence() < ExprPrecedence::Prefix,
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
@ -348,7 +339,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < parser::PREC_PREFIX,
|
||||
expr.precedence() < ExprPrecedence::Prefix,
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
@ -432,10 +423,9 @@ impl<'a> State<'a> {
|
||||
self.print_token_literal(lit, expr.span)
|
||||
}
|
||||
ast::ExprKind::Cast(expr, ty) => {
|
||||
let prec = AssocOp::As.precedence() as i8;
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < prec,
|
||||
expr.precedence() < ExprPrecedence::Cast,
|
||||
fixup.leftmost_subexpression(),
|
||||
);
|
||||
self.space();
|
||||
@ -512,7 +502,7 @@ impl<'a> State<'a> {
|
||||
MatchKind::Postfix => {
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < parser::PREC_UNAMBIGUOUS,
|
||||
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||
fixup,
|
||||
);
|
||||
self.word_nbsp(".match");
|
||||
@ -576,31 +566,31 @@ impl<'a> State<'a> {
|
||||
ast::ExprKind::Await(expr, _) => {
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < parser::PREC_UNAMBIGUOUS,
|
||||
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||
fixup,
|
||||
);
|
||||
self.word(".await");
|
||||
}
|
||||
ast::ExprKind::Assign(lhs, rhs, _) => {
|
||||
let prec = AssocOp::Assign.precedence() as i8;
|
||||
self.print_expr_cond_paren(
|
||||
lhs,
|
||||
lhs.precedence() <= prec,
|
||||
// Ranges are allowed on the right-hand side of assignment,
|
||||
// but not the left. `(a..b) = c` needs parentheses.
|
||||
lhs.precedence() <= ExprPrecedence::Range,
|
||||
fixup.leftmost_subexpression(),
|
||||
);
|
||||
self.space();
|
||||
self.word_space("=");
|
||||
self.print_expr_cond_paren(
|
||||
rhs,
|
||||
rhs.precedence() < prec,
|
||||
rhs.precedence() < ExprPrecedence::Assign,
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
ast::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||
let prec = AssocOp::Assign.precedence() as i8;
|
||||
self.print_expr_cond_paren(
|
||||
lhs,
|
||||
lhs.precedence() <= prec,
|
||||
lhs.precedence() <= ExprPrecedence::Range,
|
||||
fixup.leftmost_subexpression(),
|
||||
);
|
||||
self.space();
|
||||
@ -608,14 +598,14 @@ impl<'a> State<'a> {
|
||||
self.word_space("=");
|
||||
self.print_expr_cond_paren(
|
||||
rhs,
|
||||
rhs.precedence() < prec,
|
||||
rhs.precedence() < ExprPrecedence::Assign,
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
ast::ExprKind::Field(expr, ident) => {
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < parser::PREC_UNAMBIGUOUS,
|
||||
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||
fixup,
|
||||
);
|
||||
self.word(".");
|
||||
@ -624,7 +614,7 @@ impl<'a> State<'a> {
|
||||
ast::ExprKind::Index(expr, index, _) => {
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < parser::PREC_UNAMBIGUOUS,
|
||||
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||
fixup.leftmost_subexpression(),
|
||||
);
|
||||
self.word("[");
|
||||
@ -636,7 +626,7 @@ impl<'a> State<'a> {
|
||||
// than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`.
|
||||
// Here we use a fake precedence value so that any child with lower precedence than
|
||||
// a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
|
||||
let fake_prec = AssocOp::LOr.precedence() as i8;
|
||||
let fake_prec = ExprPrecedence::LOr;
|
||||
if let Some(e) = start {
|
||||
self.print_expr_cond_paren(
|
||||
e,
|
||||
@ -671,7 +661,7 @@ impl<'a> State<'a> {
|
||||
expr,
|
||||
// Parenthesize if required by precedence, or in the
|
||||
// case of `break 'inner: loop { break 'inner 1 } + 1`
|
||||
expr.precedence() < parser::PREC_JUMP
|
||||
expr.precedence() < ExprPrecedence::Jump
|
||||
|| (opt_label.is_none() && classify::leading_labeled_expr(expr)),
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
@ -690,7 +680,7 @@ impl<'a> State<'a> {
|
||||
self.word(" ");
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < parser::PREC_JUMP,
|
||||
expr.precedence() < ExprPrecedence::Jump,
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
@ -703,7 +693,7 @@ impl<'a> State<'a> {
|
||||
self.word(" ");
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < parser::PREC_JUMP,
|
||||
expr.precedence() < ExprPrecedence::Jump,
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
@ -713,7 +703,7 @@ impl<'a> State<'a> {
|
||||
self.word(" ");
|
||||
self.print_expr_cond_paren(
|
||||
result,
|
||||
result.precedence() < parser::PREC_JUMP,
|
||||
result.precedence() < ExprPrecedence::Jump,
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
@ -767,13 +757,13 @@ impl<'a> State<'a> {
|
||||
self.space();
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < parser::PREC_JUMP,
|
||||
expr.precedence() < ExprPrecedence::Jump,
|
||||
fixup.subsequent_subexpression(),
|
||||
);
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Try(e) => {
|
||||
self.print_expr_cond_paren(e, e.precedence() < parser::PREC_UNAMBIGUOUS, fixup);
|
||||
self.print_expr_cond_paren(e, e.precedence() < ExprPrecedence::Unambiguous, fixup);
|
||||
self.word("?")
|
||||
}
|
||||
ast::ExprKind::TryBlock(blk) => {
|
||||
|
@ -6,6 +6,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::process::ExitStatus;
|
||||
|
||||
use rustc_abi::TargetDataLayoutErrors;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_macros::Subdiagnostic;
|
||||
use rustc_span::Span;
|
||||
@ -298,6 +299,12 @@ impl IntoDiagArg for hir::def::Namespace {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDiagArg for ExprPrecedence {
|
||||
fn into_diag_arg(self) -> DiagArgValue {
|
||||
DiagArgValue::Number(self as i32)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DiagSymbolList<S = Symbol>(Vec<S>);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::fmt;
|
||||
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_ast::util::parser::{AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_UNAMBIGUOUS};
|
||||
use rustc_ast::util::parser::{AssocOp, ExprPrecedence};
|
||||
use rustc_ast::{
|
||||
self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label,
|
||||
LitKind, TraitObjectSyntax, UintTy,
|
||||
@ -1695,22 +1695,22 @@ pub struct Expr<'hir> {
|
||||
}
|
||||
|
||||
impl Expr<'_> {
|
||||
pub fn precedence(&self) -> i8 {
|
||||
pub fn precedence(&self) -> ExprPrecedence {
|
||||
match self.kind {
|
||||
ExprKind::Closure { .. } => PREC_CLOSURE,
|
||||
ExprKind::Closure { .. } => ExprPrecedence::Closure,
|
||||
|
||||
ExprKind::Break(..)
|
||||
| ExprKind::Continue(..)
|
||||
| ExprKind::Ret(..)
|
||||
| ExprKind::Yield(..)
|
||||
| ExprKind::Become(..) => PREC_JUMP,
|
||||
| ExprKind::Become(..) => ExprPrecedence::Jump,
|
||||
|
||||
// Binop-like expr kinds, handled by `AssocOp`.
|
||||
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8,
|
||||
ExprKind::Cast(..) => AssocOp::As.precedence() as i8,
|
||||
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
|
||||
ExprKind::Cast(..) => ExprPrecedence::Cast,
|
||||
|
||||
ExprKind::Assign(..) |
|
||||
ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8,
|
||||
ExprKind::AssignOp(..) => ExprPrecedence::Assign,
|
||||
|
||||
// Unary, prefix
|
||||
ExprKind::AddrOf(..)
|
||||
@ -1719,7 +1719,7 @@ impl Expr<'_> {
|
||||
// need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
|
||||
// but we need to print `(let _ = a) < b` as-is with parens.
|
||||
| ExprKind::Let(..)
|
||||
| ExprKind::Unary(..) => PREC_PREFIX,
|
||||
| ExprKind::Unary(..) => ExprPrecedence::Prefix,
|
||||
|
||||
// Never need parens
|
||||
ExprKind::Array(_)
|
||||
@ -1740,7 +1740,7 @@ impl Expr<'_> {
|
||||
| ExprKind::Struct(..)
|
||||
| ExprKind::Tup(_)
|
||||
| ExprKind::Type(..)
|
||||
| ExprKind::Err(_) => PREC_UNAMBIGUOUS,
|
||||
| ExprKind::Err(_) => ExprPrecedence::Unambiguous,
|
||||
|
||||
ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ fn allowed_union_or_unsafe_field<'tcx>(
|
||||
// Fallback case: allow `ManuallyDrop` and things that are `Copy`,
|
||||
// also no need to report an error if the type is unresolved.
|
||||
ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop())
|
||||
|| ty.is_copy_modulo_regions(tcx, typing_env)
|
||||
|| tcx.type_is_copy_modulo_regions(typing_env, ty)
|
||||
|| ty.references_error()
|
||||
}
|
||||
};
|
||||
|
@ -178,7 +178,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
|
||||
|
||||
// Check that the type implements Copy. The only case where this can
|
||||
// possibly fail is for SIMD types which don't #[derive(Copy)].
|
||||
if !ty.is_copy_modulo_regions(self.tcx, self.typing_env) {
|
||||
if !self.tcx.type_is_copy_modulo_regions(self.typing_env, ty) {
|
||||
let msg = "arguments for inline assembly must be copyable";
|
||||
self.tcx
|
||||
.dcx()
|
||||
|
@ -10,7 +10,7 @@ use std::cell::Cell;
|
||||
use std::vec;
|
||||
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_ast::util::parser::{self, AssocOp, Fixity};
|
||||
use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity};
|
||||
use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
|
||||
use rustc_ast_pretty::pp::{self, Breaks};
|
||||
use rustc_ast_pretty::pprust::{Comments, PrintState};
|
||||
@ -1125,12 +1125,12 @@ impl<'a> State<'a> {
|
||||
}
|
||||
|
||||
fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
let prec = match func.kind {
|
||||
hir::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
|
||||
_ => parser::PREC_UNAMBIGUOUS,
|
||||
let needs_paren = match func.kind {
|
||||
hir::ExprKind::Field(..) => true,
|
||||
_ => func.precedence() < ExprPrecedence::Unambiguous,
|
||||
};
|
||||
|
||||
self.print_expr_cond_paren(func, func.precedence() < prec);
|
||||
self.print_expr_cond_paren(func, needs_paren);
|
||||
self.print_call_post(args)
|
||||
}
|
||||
|
||||
@ -1141,7 +1141,7 @@ impl<'a> State<'a> {
|
||||
args: &[hir::Expr<'_>],
|
||||
) {
|
||||
let base_args = args;
|
||||
self.print_expr_cond_paren(receiver, receiver.precedence() < parser::PREC_UNAMBIGUOUS);
|
||||
self.print_expr_cond_paren(receiver, receiver.precedence() < ExprPrecedence::Unambiguous);
|
||||
self.word(".");
|
||||
self.print_ident(segment.ident);
|
||||
|
||||
@ -1155,37 +1155,38 @@ impl<'a> State<'a> {
|
||||
|
||||
fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) {
|
||||
let assoc_op = AssocOp::from_ast_binop(op.node);
|
||||
let prec = assoc_op.precedence() as i8;
|
||||
let fixity = assoc_op.fixity();
|
||||
let binop_prec = assoc_op.precedence();
|
||||
let left_prec = lhs.precedence();
|
||||
let right_prec = rhs.precedence();
|
||||
|
||||
let (left_prec, right_prec) = match fixity {
|
||||
Fixity::Left => (prec, prec + 1),
|
||||
Fixity::Right => (prec + 1, prec),
|
||||
Fixity::None => (prec + 1, prec + 1),
|
||||
let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() {
|
||||
Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
|
||||
Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
|
||||
Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
|
||||
};
|
||||
|
||||
let left_prec = match (&lhs.kind, op.node) {
|
||||
match (&lhs.kind, op.node) {
|
||||
// These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
|
||||
// the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
|
||||
// of `(x as i32) < ...`. We need to convince it _not_ to do that.
|
||||
(&hir::ExprKind::Cast { .. }, hir::BinOpKind::Lt | hir::BinOpKind::Shl) => {
|
||||
parser::PREC_FORCE_PAREN
|
||||
left_needs_paren = true;
|
||||
}
|
||||
(&hir::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
|
||||
parser::PREC_FORCE_PAREN
|
||||
(&hir::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(binop_prec) => {
|
||||
left_needs_paren = true;
|
||||
}
|
||||
_ => left_prec,
|
||||
};
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.print_expr_cond_paren(lhs, lhs.precedence() < left_prec);
|
||||
self.print_expr_cond_paren(lhs, left_needs_paren);
|
||||
self.space();
|
||||
self.word_space(op.node.as_str());
|
||||
self.print_expr_cond_paren(rhs, rhs.precedence() < right_prec)
|
||||
self.print_expr_cond_paren(rhs, right_needs_paren);
|
||||
}
|
||||
|
||||
fn print_expr_unary(&mut self, op: hir::UnOp, expr: &hir::Expr<'_>) {
|
||||
self.word(op.as_str());
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_PREFIX)
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix);
|
||||
}
|
||||
|
||||
fn print_expr_addr_of(
|
||||
@ -1202,7 +1203,7 @@ impl<'a> State<'a> {
|
||||
self.print_mutability(mutability, true);
|
||||
}
|
||||
}
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_PREFIX)
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix);
|
||||
}
|
||||
|
||||
fn print_literal(&mut self, lit: &hir::Lit) {
|
||||
@ -1340,8 +1341,7 @@ impl<'a> State<'a> {
|
||||
self.print_literal(lit);
|
||||
}
|
||||
hir::ExprKind::Cast(expr, ty) => {
|
||||
let prec = AssocOp::As.precedence() as i8;
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < prec);
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Cast);
|
||||
self.space();
|
||||
self.word_space("as");
|
||||
self.print_type(ty);
|
||||
@ -1442,27 +1442,25 @@ impl<'a> State<'a> {
|
||||
self.print_block(blk);
|
||||
}
|
||||
hir::ExprKind::Assign(lhs, rhs, _) => {
|
||||
let prec = AssocOp::Assign.precedence() as i8;
|
||||
self.print_expr_cond_paren(lhs, lhs.precedence() <= prec);
|
||||
self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign);
|
||||
self.space();
|
||||
self.word_space("=");
|
||||
self.print_expr_cond_paren(rhs, rhs.precedence() < prec);
|
||||
self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign);
|
||||
}
|
||||
hir::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||
let prec = AssocOp::Assign.precedence() as i8;
|
||||
self.print_expr_cond_paren(lhs, lhs.precedence() <= prec);
|
||||
self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign);
|
||||
self.space();
|
||||
self.word(op.node.as_str());
|
||||
self.word_space("=");
|
||||
self.print_expr_cond_paren(rhs, rhs.precedence() < prec);
|
||||
self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign);
|
||||
}
|
||||
hir::ExprKind::Field(expr, ident) => {
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_UNAMBIGUOUS);
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Unambiguous);
|
||||
self.word(".");
|
||||
self.print_ident(ident);
|
||||
}
|
||||
hir::ExprKind::Index(expr, index, _) => {
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_UNAMBIGUOUS);
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Unambiguous);
|
||||
self.word("[");
|
||||
self.print_expr(index);
|
||||
self.word("]");
|
||||
@ -1476,7 +1474,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
if let Some(expr) = opt_expr {
|
||||
self.space();
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_JUMP);
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump);
|
||||
}
|
||||
}
|
||||
hir::ExprKind::Continue(destination) => {
|
||||
@ -1490,13 +1488,13 @@ impl<'a> State<'a> {
|
||||
self.word("return");
|
||||
if let Some(expr) = result {
|
||||
self.word(" ");
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_JUMP);
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump);
|
||||
}
|
||||
}
|
||||
hir::ExprKind::Become(result) => {
|
||||
self.word("become");
|
||||
self.word(" ");
|
||||
self.print_expr_cond_paren(result, result.precedence() < parser::PREC_JUMP);
|
||||
self.print_expr_cond_paren(result, result.precedence() < ExprPrecedence::Jump);
|
||||
}
|
||||
hir::ExprKind::InlineAsm(asm) => {
|
||||
self.word("asm!");
|
||||
@ -1521,7 +1519,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
hir::ExprKind::Yield(expr, _) => {
|
||||
self.word_space("yield");
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_JUMP);
|
||||
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump);
|
||||
}
|
||||
hir::ExprKind::Err(_) => {
|
||||
self.popen();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::iter;
|
||||
|
||||
use rustc_ast::util::parser::PREC_UNAMBIGUOUS;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
|
||||
use rustc_hir::def::{self, CtorKind, Namespace, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
@ -606,7 +606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
};
|
||||
|
||||
if let Ok(rest_snippet) = rest_snippet {
|
||||
let sugg = if callee_expr.precedence() >= PREC_UNAMBIGUOUS {
|
||||
let sugg = if callee_expr.precedence() >= ExprPrecedence::Unambiguous {
|
||||
vec![
|
||||
(up_to_rcvr_span, "".to_string()),
|
||||
(rest_span, format!(".{}({rest_snippet}", segment.ident)),
|
||||
|
@ -28,6 +28,7 @@
|
||||
//! expression, `e as U2` is not necessarily so (in fact it will only be valid if
|
||||
//! `U1` coerces to `U2`).
|
||||
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
|
||||
@ -1108,7 +1109,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
||||
|
||||
fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) {
|
||||
let expr_prec = self.expr.precedence();
|
||||
let needs_parens = expr_prec < rustc_ast::util::parser::PREC_UNAMBIGUOUS;
|
||||
let needs_parens = expr_prec < ExprPrecedence::Unambiguous;
|
||||
|
||||
let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize));
|
||||
let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span);
|
||||
|
@ -228,7 +228,7 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) {
|
||||
}
|
||||
|
||||
fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_copy_modulo_regions(self.0.tcx, self.0.typing_env())
|
||||
self.0.type_is_copy_modulo_regions(ty)
|
||||
}
|
||||
|
||||
fn body_owner_def_id(&self) -> LocalDefId {
|
||||
|
@ -2,7 +2,7 @@ use core::cmp::min;
|
||||
use core::iter;
|
||||
|
||||
use hir::def_id::LocalDefId;
|
||||
use rustc_ast::util::parser::PREC_UNAMBIGUOUS;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
use rustc_errors::{Applicability, Diag, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
@ -398,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// so we remove the user's `clone` call.
|
||||
{
|
||||
vec![(receiver_method.ident.span, conversion_method.name.to_string())]
|
||||
} else if expr.precedence() < PREC_UNAMBIGUOUS {
|
||||
} else if expr.precedence() < ExprPrecedence::Unambiguous {
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), "(".to_string()),
|
||||
(expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
|
||||
@ -1376,7 +1376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
{
|
||||
let span = expr.span.find_oldest_ancestor_in_same_ctxt();
|
||||
|
||||
let mut sugg = if expr.precedence() >= PREC_UNAMBIGUOUS {
|
||||
let mut sugg = if expr.precedence() >= ExprPrecedence::Unambiguous {
|
||||
vec![(span.shrink_to_hi(), ".into()".to_owned())]
|
||||
} else {
|
||||
vec![
|
||||
@ -3000,7 +3000,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
"change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
|
||||
);
|
||||
|
||||
let close_paren = if expr.precedence() < PREC_UNAMBIGUOUS {
|
||||
let close_paren = if expr.precedence() < ExprPrecedence::Unambiguous {
|
||||
sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
|
||||
")"
|
||||
} else {
|
||||
@ -3025,7 +3025,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let len = src.trim_end_matches(&checked_ty.to_string()).len();
|
||||
expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
|
||||
},
|
||||
if expr.precedence() < PREC_UNAMBIGUOUS {
|
||||
if expr.precedence() < ExprPrecedence::Unambiguous {
|
||||
// Readd `)`
|
||||
format!("{expected_ty})")
|
||||
} else {
|
||||
|
@ -586,7 +586,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()) {
|
||||
if cx.type_is_copy_modulo_regions(ty) {
|
||||
return;
|
||||
}
|
||||
if type_implements_negative_copy_modulo_regions(cx.tcx, ty, cx.typing_env()) {
|
||||
|
@ -710,6 +710,10 @@ impl<'tcx> LateContext<'tcx> {
|
||||
TypingEnv { typing_mode: self.typing_mode(), param_env: self.param_env }
|
||||
}
|
||||
|
||||
pub fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
|
||||
self.tcx.type_is_copy_modulo_regions(self.typing_env(), ty)
|
||||
}
|
||||
|
||||
/// Gets the type-checking results for the current body,
|
||||
/// or `None` if outside a body.
|
||||
pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> {
|
||||
|
@ -144,7 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless {
|
||||
&& let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id)
|
||||
{
|
||||
let arg_ty = cx.typeck_results().expr_ty(arg);
|
||||
let is_copy = arg_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env());
|
||||
let is_copy = cx.type_is_copy_modulo_regions(arg_ty);
|
||||
let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
|
||||
let let_underscore_ignore_sugg = || {
|
||||
if let Some((_, node)) = cx.tcx.hir().parent_iter(expr.hir_id).nth(0)
|
||||
|
@ -242,17 +242,10 @@ fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &Path<'_>) -> Option<String> {
|
||||
}
|
||||
// Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait.
|
||||
Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => {
|
||||
if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind() {
|
||||
if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(adt.did())
|
||||
{
|
||||
// NOTE: This path is currently unreachable as `Ty<'tcx>` is
|
||||
// defined as a type alias meaning that `impl<'tcx> Ty<'tcx>`
|
||||
// is not actually allowed.
|
||||
//
|
||||
// I(@lcnr) still kept this branch in so we don't miss this
|
||||
// if we ever change it in the future.
|
||||
return Some(format!("{}<{}>", name, args[0]));
|
||||
}
|
||||
if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind()
|
||||
&& let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(adt.did())
|
||||
{
|
||||
return Some(format!("{}<{}>", name, args[0]));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
|
@ -172,6 +172,24 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether `ty: Copy` holds while ignoring region constraints.
|
||||
///
|
||||
/// This impacts whether values of `ty` are *moved* or *copied*
|
||||
/// when referenced. This means that we may generate MIR which
|
||||
/// does copies even when the type actually doesn't satisfy the
|
||||
/// full requirements for the `Copy` trait (cc #29149) -- this
|
||||
/// winds up being reported as an error during NLL borrow check.
|
||||
///
|
||||
/// This function should not be used if there is an `InferCtxt` available.
|
||||
/// Use `InferCtxt::type_is_copy_modulo_regions` instead.
|
||||
pub fn type_is_copy_modulo_regions(
|
||||
self,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
ty.is_trivially_pure_clone_copy() || self.is_copy_raw(typing_env.as_query_input(ty))
|
||||
}
|
||||
|
||||
/// Returns the deeply last field of nested structures, or the same type if
|
||||
/// not a structure at all. Corresponds to the only possible unsized field,
|
||||
/// and its type can be used to determine unsizing strategy.
|
||||
@ -1174,21 +1192,6 @@ impl<'tcx> Ty<'tcx> {
|
||||
.map(|(min, _)| ty::Const::from_bits(tcx, min, typing_env, self))
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` are *moved* or *copied*
|
||||
/// when referenced -- this amounts to a check for whether `T:
|
||||
/// Copy`, but note that we **don't** consider lifetimes when
|
||||
/// doing this check. This means that we may generate MIR which
|
||||
/// does copies even when the type actually doesn't satisfy the
|
||||
/// full requirements for the `Copy` trait (cc #29149) -- this
|
||||
/// winds up being reported as an error during NLL borrow check.
|
||||
pub fn is_copy_modulo_regions(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
) -> bool {
|
||||
self.is_trivially_pure_clone_copy() || tcx.is_copy_raw(typing_env.as_query_input(self))
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` have a size known at
|
||||
/// compile time (i.e., whether `T: Sized`). Lifetimes are ignored
|
||||
/// for the purposes of this check, so it can be an
|
||||
|
@ -176,7 +176,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
let ty = expr.ty;
|
||||
if !ty.is_sized(tcx, this.typing_env()) {
|
||||
// !sized means !copy, so this is an unsized move
|
||||
assert!(!ty.is_copy_modulo_regions(tcx, this.typing_env()));
|
||||
assert!(!tcx.type_is_copy_modulo_regions(this.typing_env(), ty));
|
||||
|
||||
// As described above, detect the case where we are passing a value of unsized
|
||||
// type, and that value is coming from the deref of a box.
|
||||
|
@ -780,7 +780,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat:
|
||||
return;
|
||||
};
|
||||
|
||||
let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.typing_env);
|
||||
let is_binding_by_move = |ty: Ty<'tcx>| !cx.tcx.type_is_copy_modulo_regions(cx.typing_env, ty);
|
||||
|
||||
let sess = cx.tcx.sess;
|
||||
|
||||
|
@ -220,15 +220,7 @@ impl<'tcx> Inliner<'tcx> {
|
||||
|
||||
// Normally, this shouldn't be required, but trait normalization failure can create a
|
||||
// validation ICE.
|
||||
if !validate_types(
|
||||
self.tcx,
|
||||
MirPhase::Runtime(RuntimePhase::Optimized),
|
||||
self.typing_env,
|
||||
&callee_body,
|
||||
&caller_body,
|
||||
)
|
||||
.is_empty()
|
||||
{
|
||||
if !validate_types(self.tcx, self.typing_env, &callee_body, &caller_body).is_empty() {
|
||||
return Err("failed to validate callee body");
|
||||
}
|
||||
|
||||
|
@ -292,7 +292,7 @@ fn run_passes_inner<'tcx>(
|
||||
}
|
||||
|
||||
pub(super) fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) {
|
||||
validate::Validator { when, mir_phase: body.phase }.run_pass(tcx, body);
|
||||
validate::Validator { when }.run_pass(tcx, body);
|
||||
}
|
||||
|
||||
fn dump_mir_for_pass<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, pass_name: &str, is_after: bool) {
|
||||
|
@ -29,12 +29,6 @@ enum EdgeKind {
|
||||
pub(super) struct Validator {
|
||||
/// Describes at which point in the pipeline this validation is happening.
|
||||
pub when: String,
|
||||
/// The phase for which we are upholding the dialect. If the given phase forbids a specific
|
||||
/// element, this validator will now emit errors if that specific element is encountered.
|
||||
/// Note that phases that change the dialect cause all *following* phases to check the
|
||||
/// invariants of the new dialect. A phase that changes dialects never checks the new invariants
|
||||
/// itself.
|
||||
pub mir_phase: MirPhase,
|
||||
}
|
||||
|
||||
impl<'tcx> crate::MirPass<'tcx> for Validator {
|
||||
@ -46,11 +40,9 @@ impl<'tcx> crate::MirPass<'tcx> for Validator {
|
||||
if matches!(body.source.instance, InstanceKind::Intrinsic(..) | InstanceKind::Virtual(..)) {
|
||||
return;
|
||||
}
|
||||
debug_assert_eq!(self.mir_phase, body.phase);
|
||||
let def_id = body.source.def_id();
|
||||
let mir_phase = body.phase;
|
||||
let typing_env = body.typing_env(tcx);
|
||||
let can_unwind = if mir_phase <= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
let can_unwind = if body.phase <= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
// In this case `AbortUnwindingCalls` haven't yet been executed.
|
||||
true
|
||||
} else if !tcx.def_kind(def_id).is_fn_like() {
|
||||
@ -64,9 +56,7 @@ impl<'tcx> crate::MirPass<'tcx> for Validator {
|
||||
ty::Coroutine(..) => ExternAbi::Rust,
|
||||
// No need to do MIR validation on error bodies
|
||||
ty::Error(_) => return,
|
||||
_ => {
|
||||
span_bug!(body.span, "unexpected body ty: {:?} phase {:?}", body_ty, mir_phase)
|
||||
}
|
||||
_ => span_bug!(body.span, "unexpected body ty: {body_ty:?}"),
|
||||
};
|
||||
|
||||
ty::layout::fn_can_unwind(tcx, Some(def_id), body_abi)
|
||||
@ -76,7 +66,6 @@ impl<'tcx> crate::MirPass<'tcx> for Validator {
|
||||
when: &self.when,
|
||||
body,
|
||||
tcx,
|
||||
mir_phase,
|
||||
unwind_edge_count: 0,
|
||||
reachable_blocks: traversal::reachable_as_bitset(body),
|
||||
value_cache: FxHashSet::default(),
|
||||
@ -86,7 +75,7 @@ impl<'tcx> crate::MirPass<'tcx> for Validator {
|
||||
cfg_checker.check_cleanup_control_flow();
|
||||
|
||||
// Also run the TypeChecker.
|
||||
for (location, msg) in validate_types(tcx, self.mir_phase, typing_env, body, body) {
|
||||
for (location, msg) in validate_types(tcx, typing_env, body, body) {
|
||||
cfg_checker.fail(location, msg);
|
||||
}
|
||||
|
||||
@ -107,7 +96,6 @@ struct CfgChecker<'a, 'tcx> {
|
||||
when: &'a str,
|
||||
body: &'a Body<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
mir_phase: MirPhase,
|
||||
unwind_edge_count: usize,
|
||||
reachable_blocks: BitSet<BasicBlock>,
|
||||
value_cache: FxHashSet<u128>,
|
||||
@ -294,7 +282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||
match &statement.kind {
|
||||
StatementKind::AscribeUserType(..) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`AscribeUserType` should have been removed after drop lowering phase",
|
||||
@ -302,7 +290,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
StatementKind::FakeRead(..) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`FakeRead` should have been removed after drop lowering phase",
|
||||
@ -310,17 +298,17 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
StatementKind::SetDiscriminant { .. } => {
|
||||
if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
|
||||
}
|
||||
}
|
||||
StatementKind::Deinit(..) => {
|
||||
if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(location, "`Deinit`is not allowed until deaggregation");
|
||||
}
|
||||
}
|
||||
StatementKind::Retag(kind, _) => {
|
||||
// FIXME(JakobDegen) The validator should check that `self.mir_phase <
|
||||
// FIXME(JakobDegen) The validator should check that `self.body.phase <
|
||||
// DropsLowered`. However, this causes ICEs with generation of drop shims, which
|
||||
// seem to fail to set their `MirPhase` correctly.
|
||||
if matches!(kind, RetagKind::TwoPhase) {
|
||||
@ -328,7 +316,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
StatementKind::Coverage(kind) => {
|
||||
if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup)
|
||||
if self.body.phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup)
|
||||
&& let CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. } = kind
|
||||
{
|
||||
self.fail(
|
||||
@ -391,7 +379,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
// the return edge from the call. FIXME(tmiasko): Since this is a strictly code
|
||||
// generation concern, the code generation should be responsible for handling
|
||||
// it.
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized)
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Optimized)
|
||||
&& self.is_critical_call_edge(target, unwind)
|
||||
{
|
||||
self.fail(
|
||||
@ -440,7 +428,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
if self.body.coroutine.is_none() {
|
||||
self.fail(location, "`Yield` cannot appear outside coroutine bodies");
|
||||
}
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(location, "`Yield` should have been replaced by coroutine lowering");
|
||||
}
|
||||
self.check_edge(location, *resume, EdgeKind::Normal);
|
||||
@ -449,7 +437,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
TerminatorKind::FalseEdge { real_target, imaginary_target } => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`FalseEdge` should have been removed after drop elaboration",
|
||||
@ -459,7 +447,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
self.check_edge(location, *imaginary_target, EdgeKind::Normal);
|
||||
}
|
||||
TerminatorKind::FalseUnwind { real_target, unwind } => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`FalseUnwind` should have been removed after drop elaboration",
|
||||
@ -478,7 +466,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
if self.body.coroutine.is_none() {
|
||||
self.fail(location, "`CoroutineDrop` cannot appear outside coroutine bodies");
|
||||
}
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`CoroutineDrop` should have been replaced by coroutine lowering",
|
||||
@ -532,13 +520,11 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||
/// `optimized_mir` is available.
|
||||
pub(super) fn validate_types<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
mir_phase: MirPhase,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
caller_body: &Body<'tcx>,
|
||||
) -> Vec<(Location, String)> {
|
||||
let mut type_checker =
|
||||
TypeChecker { body, caller_body, tcx, typing_env, mir_phase, failures: Vec::new() };
|
||||
let mut type_checker = TypeChecker { body, caller_body, tcx, typing_env, failures: Vec::new() };
|
||||
type_checker.visit_body(body);
|
||||
type_checker.failures
|
||||
}
|
||||
@ -548,7 +534,6 @@ struct TypeChecker<'a, 'tcx> {
|
||||
caller_body: &'a Body<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
mir_phase: MirPhase,
|
||||
failures: Vec<(Location, String)>,
|
||||
}
|
||||
|
||||
@ -577,7 +562,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
|
||||
// After borrowck subtyping should be fully explicit via
|
||||
// `Subtype` projections.
|
||||
let variance = if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
let variance = if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
Variance::Invariant
|
||||
} else {
|
||||
Variance::Covariant
|
||||
@ -618,13 +603,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
|
||||
// This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
|
||||
if self.tcx.sess.opts.unstable_opts.validate_mir
|
||||
&& self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial)
|
||||
&& self.body.phase < MirPhase::Runtime(RuntimePhase::Initial)
|
||||
{
|
||||
// `Operand::Copy` is only supposed to be used with `Copy` types.
|
||||
if let Operand::Copy(place) = operand {
|
||||
let ty = place.ty(&self.body.local_decls, self.tcx).ty;
|
||||
|
||||
if !ty.is_copy_modulo_regions(self.tcx, self.typing_env) {
|
||||
if !self.tcx.type_is_copy_modulo_regions(self.typing_env, ty) {
|
||||
self.fail(location, format!("`Operand::Copy` with non-`Copy` type {ty}"));
|
||||
}
|
||||
}
|
||||
@ -642,7 +627,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
) {
|
||||
match elem {
|
||||
ProjectionElem::OpaqueCast(ty)
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) =>
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) =>
|
||||
{
|
||||
self.fail(
|
||||
location,
|
||||
@ -656,7 +641,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
ProjectionElem::Deref
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) =>
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) =>
|
||||
{
|
||||
let base_ty = place_ref.ty(&self.body.local_decls, self.tcx).ty;
|
||||
|
||||
@ -856,7 +841,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
// Set off any `bug!`s in the type computation code
|
||||
let _ = place.ty(&self.body.local_decls, self.tcx);
|
||||
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial)
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial)
|
||||
&& place.projection.len() > 1
|
||||
&& cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo)
|
||||
&& place.projection[1..].contains(&ProjectionElem::Deref)
|
||||
@ -974,7 +959,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
AggregateKind::RawPtr(pointee_ty, mutability) => {
|
||||
if !matches!(self.mir_phase, MirPhase::Runtime(_)) {
|
||||
if !matches!(self.body.phase, MirPhase::Runtime(_)) {
|
||||
// It would probably be fine to support this in earlier phases, but at the
|
||||
// time of writing it's only ever introduced from intrinsic lowering, so
|
||||
// earlier things just `bug!` on it.
|
||||
@ -1016,7 +1001,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
},
|
||||
Rvalue::Ref(_, BorrowKind::Fake(_), _) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`Assign` statement with a `Fake` borrow should have been removed in runtime MIR",
|
||||
@ -1130,7 +1115,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
);
|
||||
}
|
||||
UnOp::PtrMetadata => {
|
||||
if !matches!(self.mir_phase, MirPhase::Runtime(_)) {
|
||||
if !matches!(self.body.phase, MirPhase::Runtime(_)) {
|
||||
// It would probably be fine to support this in earlier phases, but at
|
||||
// the time of writing it's only ever introduced from intrinsic
|
||||
// lowering or other runtime-phase optimization passes, so earlier
|
||||
@ -1206,7 +1191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
"CastKind::{kind:?} output must be a raw const pointer, not {:?}",
|
||||
ty::RawPtr(_, Mutability::Not)
|
||||
);
|
||||
if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) {
|
||||
if self.body.phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) {
|
||||
self.fail(location, format!("After borrowck, MIR disallows {kind:?}"));
|
||||
}
|
||||
}
|
||||
@ -1222,7 +1207,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
"CastKind::{kind:?} output must be a raw pointer, not {:?}",
|
||||
ty::RawPtr(..)
|
||||
);
|
||||
if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) {
|
||||
if self.body.phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) {
|
||||
self.fail(location, format!("After borrowck, MIR disallows {kind:?}"));
|
||||
}
|
||||
}
|
||||
@ -1288,7 +1273,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
CastKind::Transmute => {
|
||||
if let MirPhase::Runtime(..) = self.mir_phase {
|
||||
if let MirPhase::Runtime(..) = self.body.phase {
|
||||
// Unlike `mem::transmute`, a MIR `Transmute` is well-formed
|
||||
// for any two `Sized` types, just potentially UB to run.
|
||||
|
||||
@ -1317,7 +1302,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
location,
|
||||
format!(
|
||||
"Transmute is not supported in non-runtime phase {:?}.",
|
||||
self.mir_phase
|
||||
self.body.phase
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -1404,7 +1389,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
StatementKind::AscribeUserType(..) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`AscribeUserType` should have been removed after drop lowering phase",
|
||||
@ -1412,7 +1397,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
StatementKind::FakeRead(..) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`FakeRead` should have been removed after drop lowering phase",
|
||||
@ -1463,7 +1448,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
StatementKind::SetDiscriminant { place, .. } => {
|
||||
if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
|
||||
}
|
||||
let pty = place.ty(&self.body.local_decls, self.tcx).ty.kind();
|
||||
@ -1477,12 +1462,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
StatementKind::Deinit(..) => {
|
||||
if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(location, "`Deinit`is not allowed until deaggregation");
|
||||
}
|
||||
}
|
||||
StatementKind::Retag(kind, _) => {
|
||||
// FIXME(JakobDegen) The validator should check that `self.mir_phase <
|
||||
// FIXME(JakobDegen) The validator should check that `self.body.phase <
|
||||
// DropsLowered`. However, this causes ICEs with generation of drop shims, which
|
||||
// seem to fail to set their `MirPhase` correctly.
|
||||
if matches!(kind, RetagKind::TwoPhase) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use rustc_ast::token::Token;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_ast::{Path, Visibility};
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{
|
||||
@ -2686,7 +2687,7 @@ pub(crate) struct UnexpectedExpressionInPattern {
|
||||
/// Was a `RangePatternBound` expected?
|
||||
pub is_bound: bool,
|
||||
/// The unexpected expr's precedence (used in match arm guard suggestions).
|
||||
pub expr_precedence: i8,
|
||||
pub expr_precedence: ExprPrecedence,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
|
@ -1,7 +1,7 @@
|
||||
// ignore-tidy-filelength
|
||||
|
||||
use core::mem;
|
||||
use core::ops::ControlFlow;
|
||||
use core::ops::{Bound, ControlFlow};
|
||||
|
||||
use ast::mut_visit::{self, MutVisitor};
|
||||
use ast::token::IdentIsRaw;
|
||||
@ -10,7 +10,7 @@ use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
|
||||
use rustc_ast::util::case::Case;
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::util::parser::{AssocOp, Fixity, prec_let_scrutinee_needs_par};
|
||||
use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par};
|
||||
use rustc_ast::visit::{Visitor, walk_expr};
|
||||
use rustc_ast::{
|
||||
self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy,
|
||||
@ -120,7 +120,7 @@ impl<'a> Parser<'a> {
|
||||
r: Restrictions,
|
||||
attrs: AttrWrapper,
|
||||
) -> PResult<'a, (P<Expr>, bool)> {
|
||||
self.with_res(r, |this| this.parse_expr_assoc_with(0, attrs))
|
||||
self.with_res(r, |this| this.parse_expr_assoc_with(Bound::Unbounded, attrs))
|
||||
}
|
||||
|
||||
/// Parses an associative expression with operators of at least `min_prec` precedence.
|
||||
@ -128,7 +128,7 @@ impl<'a> Parser<'a> {
|
||||
/// followed by a subexpression (e.g. `1 + 2`).
|
||||
pub(super) fn parse_expr_assoc_with(
|
||||
&mut self,
|
||||
min_prec: usize,
|
||||
min_prec: Bound<ExprPrecedence>,
|
||||
attrs: AttrWrapper,
|
||||
) -> PResult<'a, (P<Expr>, bool)> {
|
||||
let lhs = if self.token.is_range_separator() {
|
||||
@ -144,7 +144,7 @@ impl<'a> Parser<'a> {
|
||||
/// was actually parsed.
|
||||
pub(super) fn parse_expr_assoc_rest_with(
|
||||
&mut self,
|
||||
min_prec: usize,
|
||||
min_prec: Bound<ExprPrecedence>,
|
||||
starts_stmt: bool,
|
||||
mut lhs: P<Expr>,
|
||||
) -> PResult<'a, (P<Expr>, bool)> {
|
||||
@ -163,7 +163,11 @@ impl<'a> Parser<'a> {
|
||||
self.restrictions
|
||||
};
|
||||
let prec = op.node.precedence();
|
||||
if prec < min_prec {
|
||||
if match min_prec {
|
||||
Bound::Included(min_prec) => prec < min_prec,
|
||||
Bound::Excluded(min_prec) => prec <= min_prec,
|
||||
Bound::Unbounded => false,
|
||||
} {
|
||||
break;
|
||||
}
|
||||
// Check for deprecated `...` syntax
|
||||
@ -276,16 +280,16 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
let fixity = op.fixity();
|
||||
let prec_adjustment = match fixity {
|
||||
Fixity::Right => 0,
|
||||
Fixity::Left => 1,
|
||||
let min_prec = match fixity {
|
||||
Fixity::Right => Bound::Included(prec),
|
||||
Fixity::Left => Bound::Excluded(prec),
|
||||
// We currently have no non-associative operators that are not handled above by
|
||||
// the special cases. The code is here only for future convenience.
|
||||
Fixity::None => 1,
|
||||
Fixity::None => Bound::Excluded(prec),
|
||||
};
|
||||
let (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
|
||||
let attrs = this.parse_outer_attributes()?;
|
||||
this.parse_expr_assoc_with(prec + prec_adjustment, attrs)
|
||||
this.parse_expr_assoc_with(min_prec, attrs)
|
||||
})?;
|
||||
|
||||
let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span);
|
||||
@ -451,7 +455,7 @@ impl<'a> Parser<'a> {
|
||||
/// The other two variants are handled in `parse_prefix_range_expr` below.
|
||||
fn parse_expr_range(
|
||||
&mut self,
|
||||
prec: usize,
|
||||
prec: ExprPrecedence,
|
||||
lhs: P<Expr>,
|
||||
op: AssocOp,
|
||||
cur_op_span: Span,
|
||||
@ -460,7 +464,7 @@ impl<'a> Parser<'a> {
|
||||
let maybe_lt = self.token.clone();
|
||||
let attrs = self.parse_outer_attributes()?;
|
||||
Some(
|
||||
self.parse_expr_assoc_with(prec + 1, attrs)
|
||||
self.parse_expr_assoc_with(Bound::Excluded(prec), attrs)
|
||||
.map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?
|
||||
.0,
|
||||
)
|
||||
@ -518,7 +522,7 @@ impl<'a> Parser<'a> {
|
||||
let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() {
|
||||
// RHS must be parsed with more associativity than the dots.
|
||||
let attrs = this.parse_outer_attributes()?;
|
||||
this.parse_expr_assoc_with(op.unwrap().precedence() + 1, attrs)
|
||||
this.parse_expr_assoc_with(Bound::Excluded(op.unwrap().precedence()), attrs)
|
||||
.map(|(x, _)| (lo.to(x.span), Some(x)))
|
||||
.map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?
|
||||
} else {
|
||||
@ -2643,7 +2647,8 @@ impl<'a> Parser<'a> {
|
||||
self.expect(&token::Eq)?;
|
||||
}
|
||||
let attrs = self.parse_outer_attributes()?;
|
||||
let (expr, _) = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), attrs)?;
|
||||
let (expr, _) =
|
||||
self.parse_expr_assoc_with(Bound::Excluded(prec_let_scrutinee_needs_par()), attrs)?;
|
||||
let span = lo.to(expr.span);
|
||||
Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered)))
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
use std::ops::Bound;
|
||||
|
||||
use rustc_ast::mut_visit::{self, MutVisitor};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token};
|
||||
use rustc_ast::util::parser::AssocOp;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_ast::visit::{self, Visitor};
|
||||
use rustc_ast::{
|
||||
self as ast, Arm, AttrVec, BinOpKind, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall,
|
||||
Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt,
|
||||
StmtKind,
|
||||
self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability,
|
||||
Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey};
|
||||
@ -435,8 +436,9 @@ impl<'a> Parser<'a> {
|
||||
|
||||
// Parse an associative expression such as `+ expr`, `% expr`, ...
|
||||
// Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
|
||||
let Ok((expr, _)) =
|
||||
snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel())
|
||||
let Ok((expr, _)) = snapshot
|
||||
.parse_expr_assoc_rest_with(Bound::Unbounded, false, expr)
|
||||
.map_err(|err| err.cancel())
|
||||
else {
|
||||
// We got a trailing method/operator, but that wasn't an expression.
|
||||
return None;
|
||||
@ -545,10 +547,7 @@ impl<'a> Parser<'a> {
|
||||
// HACK: a neater way would be preferable.
|
||||
let expr = match &err.args["expr_precedence"] {
|
||||
DiagArgValue::Number(expr_precedence) => {
|
||||
if *expr_precedence
|
||||
<= AssocOp::from_ast_binop(BinOpKind::Eq).precedence()
|
||||
as i32
|
||||
{
|
||||
if *expr_precedence <= ExprPrecedence::Compare as i32 {
|
||||
format!("({expr})")
|
||||
} else {
|
||||
format!("{expr}")
|
||||
@ -570,9 +569,7 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
Some(guard) => {
|
||||
// Are parentheses required around the old guard?
|
||||
let wrap_guard = guard.precedence()
|
||||
<= AssocOp::from_ast_binop(BinOpKind::And).precedence()
|
||||
as i8;
|
||||
let wrap_guard = guard.precedence() <= ExprPrecedence::LAnd;
|
||||
|
||||
err.subdiagnostic(
|
||||
UnexpectedExpressionInPatternSugg::UpdateGuard {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::borrow::Cow;
|
||||
use std::mem;
|
||||
use std::ops::Bound;
|
||||
|
||||
use ast::Label;
|
||||
use rustc_ast as ast;
|
||||
@ -207,7 +208,7 @@ impl<'a> Parser<'a> {
|
||||
// Perform this outside of the `collect_tokens` closure, since our
|
||||
// outer attributes do not apply to this part of the expression.
|
||||
let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| {
|
||||
this.parse_expr_assoc_rest_with(0, true, expr)
|
||||
this.parse_expr_assoc_rest_with(Bound::Unbounded, true, expr)
|
||||
})?;
|
||||
Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
|
||||
} else {
|
||||
@ -240,7 +241,7 @@ impl<'a> Parser<'a> {
|
||||
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
|
||||
let e = self.maybe_recover_from_bad_qpath(e)?;
|
||||
let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?;
|
||||
let (e, _) = self.parse_expr_assoc_rest_with(0, false, e)?;
|
||||
let (e, _) = self.parse_expr_assoc_rest_with(Bound::Unbounded, false, e)?;
|
||||
StmtKind::Expr(e)
|
||||
};
|
||||
Ok(self.mk_stmt(lo.to(hi), kind))
|
||||
|
@ -1358,6 +1358,12 @@ pub struct EarlyDiagCtxt {
|
||||
dcx: DiagCtxt,
|
||||
}
|
||||
|
||||
impl Default for EarlyDiagCtxt {
|
||||
fn default() -> Self {
|
||||
Self::new(ErrorOutputType::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyDiagCtxt {
|
||||
pub fn new(output: ErrorOutputType) -> Self {
|
||||
let emitter = mk_emitter(output);
|
||||
|
@ -35,7 +35,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
// FIXME(#132279): This should be removed as it causes us to incorrectly
|
||||
// handle opaques in their defining scope.
|
||||
if !(param_env, ty).has_infer() {
|
||||
return ty.is_copy_modulo_regions(self.tcx, self.typing_env(param_env));
|
||||
return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty);
|
||||
}
|
||||
|
||||
let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None);
|
||||
|
@ -202,7 +202,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
_ if component.is_copy_modulo_regions(tcx, self.typing_env) => {}
|
||||
_ if tcx.type_is_copy_modulo_regions(self.typing_env, component) => {}
|
||||
|
||||
ty::Closure(_, args) => {
|
||||
for upvar in args.as_closure().upvar_tys() {
|
||||
|
@ -858,7 +858,7 @@ impl<T> [T] {
|
||||
|
||||
/// Gets a reference to the underlying array.
|
||||
///
|
||||
/// If `N` is not exactly equal to slice's the length of `self`, then this method returns `None`.
|
||||
/// If `N` is not exactly equal to the length of `self`, then this method returns `None`.
|
||||
#[unstable(feature = "slice_as_array", issue = "133508")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
|
@ -32,12 +32,11 @@ use std::iter::Peekable;
|
||||
use std::ops::{ControlFlow, Range};
|
||||
use std::path::PathBuf;
|
||||
use std::str::{self, CharIndices};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use pulldown_cmark::{
|
||||
BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html,
|
||||
};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{Diag, DiagMessage};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
@ -1882,66 +1881,63 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<Rust
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct IdMap {
|
||||
map: FxHashMap<Cow<'static, str>, usize>,
|
||||
map: FxHashMap<String, usize>,
|
||||
existing_footnotes: usize,
|
||||
}
|
||||
|
||||
// The map is pre-initialized and then can be used as is to prevent cloning it for each item
|
||||
// (in the sidebar rendering).
|
||||
static DEFAULT_ID_MAP: OnceLock<FxHashSet<&'static str>> = OnceLock::new();
|
||||
|
||||
fn init_id_map() -> FxHashSet<&'static str> {
|
||||
let mut map = FxHashSet::default();
|
||||
// This is the list of IDs used in JavaScript.
|
||||
map.insert("help");
|
||||
map.insert("settings");
|
||||
map.insert("not-displayed");
|
||||
map.insert("alternative-display");
|
||||
map.insert("search");
|
||||
map.insert("crate-search");
|
||||
map.insert("crate-search-div");
|
||||
// This is the list of IDs used in HTML generated in Rust (including the ones
|
||||
// used in tera template files).
|
||||
map.insert("themeStyle");
|
||||
map.insert("settings-menu");
|
||||
map.insert("help-button");
|
||||
map.insert("sidebar-button");
|
||||
map.insert("main-content");
|
||||
map.insert("toggle-all-docs");
|
||||
map.insert("all-types");
|
||||
map.insert("default-settings");
|
||||
map.insert("sidebar-vars");
|
||||
map.insert("copy-path");
|
||||
map.insert("rustdoc-toc");
|
||||
map.insert("rustdoc-modnav");
|
||||
// This is the list of IDs used by rustdoc sections (but still generated by
|
||||
// rustdoc).
|
||||
map.insert("fields");
|
||||
map.insert("variants");
|
||||
map.insert("implementors-list");
|
||||
map.insert("synthetic-implementors-list");
|
||||
map.insert("foreign-impls");
|
||||
map.insert("implementations");
|
||||
map.insert("trait-implementations");
|
||||
map.insert("synthetic-implementations");
|
||||
map.insert("blanket-implementations");
|
||||
map.insert("required-associated-types");
|
||||
map.insert("provided-associated-types");
|
||||
map.insert("provided-associated-consts");
|
||||
map.insert("required-associated-consts");
|
||||
map.insert("required-methods");
|
||||
map.insert("provided-methods");
|
||||
map.insert("dyn-compatibility");
|
||||
map.insert("implementors");
|
||||
map.insert("synthetic-implementors");
|
||||
map.insert("implementations-list");
|
||||
map.insert("trait-implementations-list");
|
||||
map.insert("synthetic-implementations-list");
|
||||
map.insert("blanket-implementations-list");
|
||||
map.insert("deref-methods");
|
||||
map.insert("layout");
|
||||
map.insert("aliased-type");
|
||||
map
|
||||
fn is_default_id(id: &str) -> bool {
|
||||
matches!(
|
||||
id,
|
||||
// This is the list of IDs used in JavaScript.
|
||||
"help"
|
||||
| "settings"
|
||||
| "not-displayed"
|
||||
| "alternative-display"
|
||||
| "search"
|
||||
| "crate-search"
|
||||
| "crate-search-div"
|
||||
// This is the list of IDs used in HTML generated in Rust (including the ones
|
||||
// used in tera template files).
|
||||
| "themeStyle"
|
||||
| "settings-menu"
|
||||
| "help-button"
|
||||
| "sidebar-button"
|
||||
| "main-content"
|
||||
| "toggle-all-docs"
|
||||
| "all-types"
|
||||
| "default-settings"
|
||||
| "sidebar-vars"
|
||||
| "copy-path"
|
||||
| "rustdoc-toc"
|
||||
| "rustdoc-modnav"
|
||||
// This is the list of IDs used by rustdoc sections (but still generated by
|
||||
// rustdoc).
|
||||
| "fields"
|
||||
| "variants"
|
||||
| "implementors-list"
|
||||
| "synthetic-implementors-list"
|
||||
| "foreign-impls"
|
||||
| "implementations"
|
||||
| "trait-implementations"
|
||||
| "synthetic-implementations"
|
||||
| "blanket-implementations"
|
||||
| "required-associated-types"
|
||||
| "provided-associated-types"
|
||||
| "provided-associated-consts"
|
||||
| "required-associated-consts"
|
||||
| "required-methods"
|
||||
| "provided-methods"
|
||||
| "dyn-compatibility"
|
||||
| "implementors"
|
||||
| "synthetic-implementors"
|
||||
| "implementations-list"
|
||||
| "trait-implementations-list"
|
||||
| "synthetic-implementations-list"
|
||||
| "blanket-implementations-list"
|
||||
| "deref-methods"
|
||||
| "layout"
|
||||
| "aliased-type"
|
||||
)
|
||||
}
|
||||
|
||||
impl IdMap {
|
||||
@ -1953,7 +1949,7 @@ impl IdMap {
|
||||
let id = match self.map.get_mut(candidate.as_ref()) {
|
||||
None => {
|
||||
let candidate = candidate.to_string();
|
||||
if DEFAULT_ID_MAP.get_or_init(init_id_map).contains(candidate.as_str()) {
|
||||
if is_default_id(&candidate) {
|
||||
let id = format!("{}-{}", candidate, 1);
|
||||
self.map.insert(candidate.into(), 2);
|
||||
id
|
||||
|
@ -321,8 +321,8 @@ fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum {
|
||||
MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), renderer)),
|
||||
TyMethodItem(m) => ItemEnum::Function(from_function(m, false, header.unwrap(), renderer)),
|
||||
ImplItem(i) => ItemEnum::Impl((*i).into_json(renderer)),
|
||||
StaticItem(s) => ItemEnum::Static(s.into_json(renderer)),
|
||||
ForeignStaticItem(s, _) => ItemEnum::Static(s.into_json(renderer)),
|
||||
StaticItem(s) => ItemEnum::Static(convert_static(s, rustc_hir::Safety::Safe, renderer)),
|
||||
ForeignStaticItem(s, safety) => ItemEnum::Static(convert_static(s, safety, renderer)),
|
||||
ForeignTypeItem => ItemEnum::ExternType,
|
||||
TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_json(renderer)),
|
||||
// FIXME(generic_const_items): Add support for generic free consts
|
||||
@ -831,17 +831,20 @@ impl FromClean<Box<clean::TypeAlias>> for TypeAlias {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromClean<clean::Static> for Static {
|
||||
fn from_clean(stat: clean::Static, renderer: &JsonRenderer<'_>) -> Self {
|
||||
let tcx = renderer.tcx;
|
||||
Static {
|
||||
type_: (*stat.type_).into_json(renderer),
|
||||
is_mutable: stat.mutability == ast::Mutability::Mut,
|
||||
expr: stat
|
||||
.expr
|
||||
.map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e)))
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
fn convert_static(
|
||||
stat: clean::Static,
|
||||
safety: rustc_hir::Safety,
|
||||
renderer: &JsonRenderer<'_>,
|
||||
) -> Static {
|
||||
let tcx = renderer.tcx;
|
||||
Static {
|
||||
type_: (*stat.type_).into_json(renderer),
|
||||
is_mutable: stat.mutability == ast::Mutability::Mut,
|
||||
is_unsafe: safety == rustc_hir::Safety::Unsafe,
|
||||
expr: stat
|
||||
.expr
|
||||
.map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e)))
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
|
||||
/// This integer is incremented with every breaking change to the API,
|
||||
/// and is returned along with the JSON blob as [`Crate::format_version`].
|
||||
/// Consuming code should assert that this value matches the format version(s) that it supports.
|
||||
pub const FORMAT_VERSION: u32 = 36;
|
||||
pub const FORMAT_VERSION: u32 = 37;
|
||||
|
||||
/// The root of the emitted JSON blob.
|
||||
///
|
||||
@ -1238,6 +1238,22 @@ pub struct Static {
|
||||
///
|
||||
/// It's not guaranteed that it'll match the actual source code for the initial value.
|
||||
pub expr: String,
|
||||
|
||||
/// Is the static `unsafe`?
|
||||
///
|
||||
/// This is only true if it's in an `extern` block, and not explicity marked
|
||||
/// as `safe`.
|
||||
///
|
||||
/// ```rust
|
||||
/// unsafe extern {
|
||||
/// static A: i32; // unsafe
|
||||
/// safe static B: i32; // safe
|
||||
/// }
|
||||
///
|
||||
/// static C: i32 = 0; // safe
|
||||
/// static mut D: i32 = 0; // safe
|
||||
/// ```
|
||||
pub is_unsafe: bool,
|
||||
}
|
||||
|
||||
/// A primitive type declaration. Declarations of this kind can only come from the core library.
|
||||
|
@ -7,7 +7,7 @@ use clippy_utils::{
|
||||
peel_middle_ty_refs,
|
||||
};
|
||||
use core::mem;
|
||||
use rustc_ast::util::parser::{PREC_PREFIX, PREC_UNAMBIGUOUS};
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
@ -963,7 +963,7 @@ fn report<'tcx>(
|
||||
// expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's
|
||||
// `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary.
|
||||
/*
|
||||
expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < PREC_PREFIX {
|
||||
expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < ExprPrecedence::Prefix {
|
||||
Cow::Owned(format!("({expr_str})"))
|
||||
} else {
|
||||
expr_str
|
||||
@ -999,13 +999,13 @@ fn report<'tcx>(
|
||||
data.first_expr.span,
|
||||
state.msg,
|
||||
|diag| {
|
||||
let (precedence, calls_field) = match cx.tcx.parent_hir_node(data.first_expr.hir_id) {
|
||||
let needs_paren = match cx.tcx.parent_hir_node(data.first_expr.hir_id) {
|
||||
Node::Expr(e) => match e.kind {
|
||||
ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false),
|
||||
ExprKind::Call(..) => (PREC_UNAMBIGUOUS, matches!(expr.kind, ExprKind::Field(..))),
|
||||
_ => (e.precedence(), false),
|
||||
ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => false,
|
||||
ExprKind::Call(..) => expr.precedence() < ExprPrecedence::Unambiguous || matches!(expr.kind, ExprKind::Field(..)),
|
||||
_ => expr.precedence() < e.precedence(),
|
||||
},
|
||||
_ => (0, false),
|
||||
_ => false,
|
||||
};
|
||||
let is_in_tuple = matches!(
|
||||
get_parent_expr(cx, data.first_expr),
|
||||
@ -1016,7 +1016,7 @@ fn report<'tcx>(
|
||||
);
|
||||
|
||||
let sugg = if !snip_is_macro
|
||||
&& (calls_field || expr.precedence() < precedence)
|
||||
&& needs_paren
|
||||
&& !has_enclosing_paren(&snip)
|
||||
&& !is_in_tuple
|
||||
{
|
||||
@ -1049,16 +1049,16 @@ fn report<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
let (prefix, precedence) = match mutability {
|
||||
let (prefix, needs_paren) = match mutability {
|
||||
Some(mutability) if !ty.is_ref() => {
|
||||
let prefix = match mutability {
|
||||
Mutability::Not => "&",
|
||||
Mutability::Mut => "&mut ",
|
||||
};
|
||||
(prefix, PREC_PREFIX)
|
||||
(prefix, expr.precedence() < ExprPrecedence::Prefix)
|
||||
},
|
||||
None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", 0),
|
||||
_ => ("", 0),
|
||||
None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", false),
|
||||
_ => ("", false),
|
||||
};
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
@ -1070,7 +1070,7 @@ fn report<'tcx>(
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let (snip, snip_is_macro) =
|
||||
snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
|
||||
let sugg = if !snip_is_macro && expr.precedence() < precedence && !has_enclosing_paren(&snip) {
|
||||
let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) {
|
||||
format!("{prefix}({snip})")
|
||||
} else {
|
||||
format!("{prefix}{snip}")
|
||||
@ -1157,7 +1157,7 @@ impl<'tcx> Dereferencing<'tcx> {
|
||||
},
|
||||
Some(parent) if !parent.span.from_expansion() => {
|
||||
// Double reference might be needed at this point.
|
||||
if parent.precedence() == PREC_UNAMBIGUOUS {
|
||||
if parent.precedence() == ExprPrecedence::Unambiguous {
|
||||
// Parentheses would be needed here, don't lint.
|
||||
*outer_pat = None;
|
||||
} else {
|
||||
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, snippet, snippet_with_applicability};
|
||||
use clippy_utils::visitors::contains_break_or_continue;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_ast::util::parser::PREC_PREFIX;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind, is_range_literal};
|
||||
use rustc_lint::LateContext;
|
||||
@ -84,7 +84,7 @@ pub(super) fn check<'tcx>(
|
||||
if !prefix.is_empty()
|
||||
&& (
|
||||
// Precedence of internal expression is less than or equal to precedence of `&expr`.
|
||||
arg_expression.precedence() <= PREC_PREFIX || is_range_literal(arg_expression)
|
||||
arg_expression.precedence() <= ExprPrecedence::Prefix || is_range_literal(arg_expression)
|
||||
)
|
||||
{
|
||||
arg_snip = format!("({arg_snip})").into();
|
||||
|
@ -7,7 +7,7 @@ use clippy_utils::{
|
||||
CaptureKind, can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res,
|
||||
path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while,
|
||||
};
|
||||
use rustc_ast::util::parser::PREC_UNAMBIGUOUS;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::def::Res;
|
||||
@ -117,7 +117,7 @@ where
|
||||
// it's being passed by value.
|
||||
let scrutinee = peel_hir_expr_refs(scrutinee).0;
|
||||
let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
|
||||
let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < PREC_UNAMBIGUOUS {
|
||||
let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < ExprPrecedence::Unambiguous {
|
||||
format!("({scrutinee_str})")
|
||||
} else {
|
||||
scrutinee_str.into()
|
||||
|
@ -148,7 +148,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
requires_copy |= !ty.is_copy_modulo_regions(cx.tcx, cx.typing_env());
|
||||
requires_copy |= !cx.type_is_copy_modulo_regions(ty);
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -158,7 +158,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
|
||||
}
|
||||
|
||||
if can_lint
|
||||
&& (!requires_copy || arg_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()))
|
||||
&& (!requires_copy || cx.type_is_copy_modulo_regions(arg_ty))
|
||||
// This case could be handled, but a fair bit of care would need to be taken.
|
||||
&& (!requires_deref || arg_ty.is_freeze(cx.tcx, cx.typing_env()))
|
||||
{
|
||||
|
@ -2,7 +2,7 @@ use clippy_utils::consts::{self, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use rustc_ast::util::parser::PREC_PREFIX;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
@ -58,7 +58,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability);
|
||||
let suggestion = if !from_macro && exp.precedence() < PREC_PREFIX && !has_enclosing_paren(&snip) {
|
||||
let suggestion = if !from_macro && exp.precedence() < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) {
|
||||
format!("-({snip})")
|
||||
} else {
|
||||
format!("-{snip}")
|
||||
|
@ -251,7 +251,7 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
|
||||
let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env())
|
||||
let by_ref = !cx.type_is_copy_modulo_regions(caller_ty)
|
||||
&& !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
|
||||
let sugg = if let Some(else_inner) = r#else {
|
||||
if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
|
||||
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use clippy_utils::{get_parent_expr, peel_middle_ty_refs};
|
||||
use rustc_ast::util::parser::PREC_PREFIX;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
||||
@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
|
||||
let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr));
|
||||
let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed));
|
||||
let parent_expr = get_parent_expr(cx, expr);
|
||||
let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence() > PREC_PREFIX);
|
||||
let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence() > ExprPrecedence::Prefix);
|
||||
|
||||
if expr_ty == indexed_ty {
|
||||
if expr_ref_count > indexed_ref_count {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_ast::util::parser::AssocOp;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Node};
|
||||
use rustc_hir_typeck::cast::check_cast;
|
||||
@ -44,8 +44,7 @@ pub(super) fn check<'tcx>(
|
||||
};
|
||||
|
||||
if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id)
|
||||
&& parent.precedence()
|
||||
> i8::try_from(AssocOp::As.precedence()).expect("AssocOp always returns a precedence < 128")
|
||||
&& parent.precedence() > ExprPrecedence::Cast
|
||||
{
|
||||
sugg = format!("({sugg})");
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ pub use type_certainty::expr_type_is_certain;
|
||||
|
||||
/// Checks if the given type implements copy.
|
||||
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_copy_modulo_regions(cx.tcx, cx.typing_env())
|
||||
cx.type_is_copy_modulo_regions(ty)
|
||||
}
|
||||
|
||||
/// This checks whether a given type is known to implement Debug.
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::OsString;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
@ -486,6 +486,9 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// Known widths of `target_has_atomic`.
|
||||
pub const KNOWN_TARGET_HAS_ATOMIC_WIDTHS: &[&str] = &["8", "16", "32", "64", "128", "ptr"];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TargetCfgs {
|
||||
pub current: TargetCfg,
|
||||
@ -611,6 +614,17 @@ impl TargetCfgs {
|
||||
("panic", Some("abort")) => cfg.panic = PanicStrategy::Abort,
|
||||
("panic", Some("unwind")) => cfg.panic = PanicStrategy::Unwind,
|
||||
("panic", other) => panic!("unexpected value for panic cfg: {other:?}"),
|
||||
|
||||
("target_has_atomic", Some(width))
|
||||
if KNOWN_TARGET_HAS_ATOMIC_WIDTHS.contains(&width) =>
|
||||
{
|
||||
cfg.target_has_atomic.insert(width.to_string());
|
||||
}
|
||||
("target_has_atomic", Some(other)) => {
|
||||
panic!("unexpected value for `target_has_atomic` cfg: {other:?}")
|
||||
}
|
||||
// Nightly-only std-internal impl detail.
|
||||
("target_has_atomic", None) => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -645,6 +659,12 @@ pub struct TargetCfg {
|
||||
pub(crate) xray: bool,
|
||||
#[serde(default = "default_reloc_model")]
|
||||
pub(crate) relocation_model: String,
|
||||
|
||||
// Not present in target cfg json output, additional derived information.
|
||||
#[serde(skip)]
|
||||
/// Supported target atomic widths: e.g. `8` to `128` or `ptr`. This is derived from the builtin
|
||||
/// `target_has_atomic` `cfg`s e.g. `target_has_atomic="8"`.
|
||||
pub(crate) target_has_atomic: BTreeSet<String>,
|
||||
}
|
||||
|
||||
impl TargetCfg {
|
||||
|
@ -17,7 +17,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
||||
"check-run-results",
|
||||
"check-stdout",
|
||||
"check-test-line-numbers-match",
|
||||
"compare-output-lines-by-subset",
|
||||
"compile-flags",
|
||||
"doc-flags",
|
||||
"dont-check-compiler-stderr",
|
||||
@ -154,6 +153,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
||||
"needs-sanitizer-thread",
|
||||
"needs-std-debug-assertions",
|
||||
"needs-symlink",
|
||||
"needs-target-has-atomic",
|
||||
"needs-threads",
|
||||
"needs-unwind",
|
||||
"needs-wasmtime",
|
||||
|
@ -115,9 +115,6 @@ pub struct TestProps {
|
||||
pub dont_check_compiler_stdout: bool,
|
||||
// For UI tests, allows compiler to generate arbitrary output to stderr
|
||||
pub dont_check_compiler_stderr: bool,
|
||||
// When checking the output of stdout or stderr check
|
||||
// that the lines of expected output are a subset of the actual output.
|
||||
pub compare_output_lines_by_subset: bool,
|
||||
// Don't force a --crate-type=dylib flag on the command line
|
||||
//
|
||||
// Set this for example if you have an auxiliary test file that contains
|
||||
@ -240,7 +237,6 @@ mod directives {
|
||||
pub const KNOWN_BUG: &'static str = "known-bug";
|
||||
pub const TEST_MIR_PASS: &'static str = "test-mir-pass";
|
||||
pub const REMAP_SRC_BASE: &'static str = "remap-src-base";
|
||||
pub const COMPARE_OUTPUT_LINES_BY_SUBSET: &'static str = "compare-output-lines-by-subset";
|
||||
pub const LLVM_COV_FLAGS: &'static str = "llvm-cov-flags";
|
||||
pub const FILECHECK_FLAGS: &'static str = "filecheck-flags";
|
||||
pub const NO_AUTO_CHECK_CFG: &'static str = "no-auto-check-cfg";
|
||||
@ -274,7 +270,6 @@ impl TestProps {
|
||||
check_run_results: false,
|
||||
dont_check_compiler_stdout: false,
|
||||
dont_check_compiler_stderr: false,
|
||||
compare_output_lines_by_subset: false,
|
||||
no_prefer_dynamic: false,
|
||||
pretty_mode: "normal".to_string(),
|
||||
pretty_compare_only: false,
|
||||
@ -550,11 +545,6 @@ impl TestProps {
|
||||
|s| s.trim().to_string(),
|
||||
);
|
||||
config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base);
|
||||
config.set_name_directive(
|
||||
ln,
|
||||
COMPARE_OUTPUT_LINES_BY_SUBSET,
|
||||
&mut self.compare_output_lines_by_subset,
|
||||
);
|
||||
|
||||
if let Some(flags) = config.parse_name_value_directive(ln, LLVM_COV_FLAGS) {
|
||||
self.llvm_cov_flags.extend(split_flags(&flags));
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::common::{Config, Sanitizer};
|
||||
use crate::common::{Config, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer};
|
||||
use crate::header::{IgnoreDecision, llvm_has_libzstd};
|
||||
|
||||
pub(super) fn handle_needs(
|
||||
@ -171,11 +171,54 @@ pub(super) fn handle_needs(
|
||||
},
|
||||
];
|
||||
|
||||
let (name, comment) = match ln.split_once([':', ' ']) {
|
||||
Some((name, comment)) => (name, Some(comment)),
|
||||
let (name, rest) = match ln.split_once([':', ' ']) {
|
||||
Some((name, rest)) => (name, Some(rest)),
|
||||
None => (ln, None),
|
||||
};
|
||||
|
||||
// FIXME(jieyouxu): tighten up this parsing to reject using both `:` and ` ` as means to
|
||||
// delineate value.
|
||||
if name == "needs-target-has-atomic" {
|
||||
let Some(rest) = rest else {
|
||||
return IgnoreDecision::Error {
|
||||
message: "expected `needs-target-has-atomic` to have a comma-separated list of atomic widths".to_string(),
|
||||
};
|
||||
};
|
||||
|
||||
// Expect directive value to be a list of comma-separated atomic widths.
|
||||
let specified_widths = rest
|
||||
.split(',')
|
||||
.map(|width| width.trim())
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
for width in &specified_widths {
|
||||
if !KNOWN_TARGET_HAS_ATOMIC_WIDTHS.contains(&width.as_str()) {
|
||||
return IgnoreDecision::Error {
|
||||
message: format!(
|
||||
"unknown width specified in `needs-target-has-atomic`: `{width}` is not a \
|
||||
known `target_has_atomic_width`, known values are `{:?}`",
|
||||
KNOWN_TARGET_HAS_ATOMIC_WIDTHS
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let satisfies_all_specified_widths = specified_widths
|
||||
.iter()
|
||||
.all(|specified| config.target_cfg().target_has_atomic.contains(specified));
|
||||
if satisfies_all_specified_widths {
|
||||
return IgnoreDecision::Continue;
|
||||
} else {
|
||||
return IgnoreDecision::Ignore {
|
||||
reason: format!(
|
||||
"skipping test as target does not support all of the required `target_has_atomic` widths `{:?}`",
|
||||
specified_widths
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if !name.starts_with("needs-") {
|
||||
return IgnoreDecision::Continue;
|
||||
}
|
||||
@ -193,7 +236,7 @@ pub(super) fn handle_needs(
|
||||
break;
|
||||
} else {
|
||||
return IgnoreDecision::Ignore {
|
||||
reason: if let Some(comment) = comment {
|
||||
reason: if let Some(comment) = rest {
|
||||
format!("{} ({})", need.ignore_reason, comment.trim())
|
||||
} else {
|
||||
need.ignore_reason.into()
|
||||
|
@ -787,3 +787,40 @@ fn test_not_trailing_directive() {
|
||||
run_path(&mut poisoned, Path::new("a.rs"), b"//@ revisions: incremental");
|
||||
assert!(!poisoned);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_needs_target_has_atomic() {
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
// `x86_64-unknown-linux-gnu` supports `["8", "16", "32", "64", "ptr"]` but not `128`.
|
||||
|
||||
let config = cfg().target("x86_64-unknown-linux-gnu").build();
|
||||
// Expectation sanity check.
|
||||
assert_eq!(
|
||||
config.target_cfg().target_has_atomic,
|
||||
BTreeSet::from([
|
||||
"8".to_string(),
|
||||
"16".to_string(),
|
||||
"32".to_string(),
|
||||
"64".to_string(),
|
||||
"ptr".to_string()
|
||||
]),
|
||||
"expected `x86_64-unknown-linux-gnu` to not have 128-bit atomic support"
|
||||
);
|
||||
|
||||
assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 8"));
|
||||
assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 16"));
|
||||
assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 32"));
|
||||
assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 64"));
|
||||
assert!(!check_ignore(&config, "//@ needs-target-has-atomic: ptr"));
|
||||
|
||||
assert!(check_ignore(&config, "//@ needs-target-has-atomic: 128"));
|
||||
|
||||
assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 8,16,32,64,ptr"));
|
||||
|
||||
assert!(check_ignore(&config, "//@ needs-target-has-atomic: 8,16,32,64,ptr,128"));
|
||||
|
||||
// Check whitespace between widths is permitted.
|
||||
assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 8, ptr"));
|
||||
assert!(check_ignore(&config, "//@ needs-target-has-atomic: 8, ptr, 128"));
|
||||
}
|
||||
|
@ -2541,13 +2541,10 @@ impl<'test> TestCx<'test> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If `compare-output-lines-by-subset` is not explicitly enabled then
|
||||
// auto-enable it when a `runner` is in use since wrapper tools might
|
||||
// provide extra output on failure, for example a WebAssembly runtime
|
||||
// might print the stack trace of an `unreachable` instruction by
|
||||
// default.
|
||||
let compare_output_by_lines =
|
||||
self.props.compare_output_lines_by_subset || self.config.runner.is_some();
|
||||
// Wrapper tools set by `runner` might provide extra output on failure,
|
||||
// for example a WebAssembly runtime might print the stack trace of an
|
||||
// `unreachable` instruction by default.
|
||||
let compare_output_by_lines = self.config.runner.is_some();
|
||||
|
||||
let tmp;
|
||||
let (expected, actual): (&str, &str) = if compare_output_by_lines {
|
||||
|
15
tests/rustdoc-json/impls/trait-for-dyn-trait.rs
Normal file
15
tests/rustdoc-json/impls/trait-for-dyn-trait.rs
Normal file
@ -0,0 +1,15 @@
|
||||
//@ set t1 = '$.index[*][?(@.name=="T1")].id'
|
||||
pub trait T1 {}
|
||||
|
||||
//@ set t2 = '$.index[*][?(@.name=="T2")].id'
|
||||
pub trait T2 {}
|
||||
|
||||
/// Fun impl
|
||||
impl T1 for dyn T2 {}
|
||||
|
||||
//@ set impl = '$.index[*][?(@.docs=="Fun impl")].id'
|
||||
//@ is '$.index[*][?(@.name=="T1")].inner.trait.implementations[*]' $impl
|
||||
//@ is '$.index[*][?(@.name=="T2")].inner.trait.implementations' []
|
||||
|
||||
//@ is '$.index[*][?(@.docs=="Fun impl")].inner.impl.trait.id' $t1
|
||||
//@ is '$.index[*][?(@.docs=="Fun impl")].inner.impl.for.dyn_trait.traits[*].trait.id' $t2
|
39
tests/rustdoc-json/statics/extern.rs
Normal file
39
tests/rustdoc-json/statics/extern.rs
Normal file
@ -0,0 +1,39 @@
|
||||
// ignore-tidy-linelength
|
||||
//@ edition: 2021
|
||||
|
||||
extern "C" {
|
||||
//@ is '$.index[*][?(@.name=="A")].inner.static.is_unsafe' true
|
||||
//@ is '$.index[*][?(@.name=="A")].inner.static.is_mutable' false
|
||||
pub static A: i32;
|
||||
//@ is '$.index[*][?(@.name=="B")].inner.static.is_unsafe' true
|
||||
//@ is '$.index[*][?(@.name=="B")].inner.static.is_mutable' true
|
||||
pub static mut B: i32;
|
||||
|
||||
// items in unadorned `extern` blocks cannot have safety qualifiers
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
//@ is '$.index[*][?(@.name=="C")].inner.static.is_unsafe' true
|
||||
//@ is '$.index[*][?(@.name=="C")].inner.static.is_mutable' false
|
||||
pub static C: i32;
|
||||
//@ is '$.index[*][?(@.name=="D")].inner.static.is_unsafe' true
|
||||
//@ is '$.index[*][?(@.name=="D")].inner.static.is_mutable' true
|
||||
pub static mut D: i32;
|
||||
|
||||
//@ is '$.index[*][?(@.name=="E")].inner.static.is_unsafe' false
|
||||
//@ is '$.index[*][?(@.name=="E")].inner.static.is_mutable' false
|
||||
pub safe static E: i32;
|
||||
//@ is '$.index[*][?(@.name=="F")].inner.static.is_unsafe' false
|
||||
//@ is '$.index[*][?(@.name=="F")].inner.static.is_mutable' true
|
||||
pub safe static mut F: i32;
|
||||
|
||||
//@ is '$.index[*][?(@.name=="G")].inner.static.is_unsafe' true
|
||||
//@ is '$.index[*][?(@.name=="G")].inner.static.is_mutable' false
|
||||
pub unsafe static G: i32;
|
||||
//@ is '$.index[*][?(@.name=="H")].inner.static.is_unsafe' true
|
||||
//@ is '$.index[*][?(@.name=="H")].inner.static.is_mutable' true
|
||||
pub unsafe static mut H: i32;
|
||||
}
|
||||
|
||||
//@ ismany '$.index[*][?(@.inner.static)].inner.static.expr' '""' '""' '""' '""' '""' '""' '""' '""'
|
||||
//@ ismany '$.index[*][?(@.inner.static)].inner.static.type.primitive' '"i32"' '"i32"' '"i32"' '"i32"' '"i32"' '"i32"' '"i32"' '"i32"'
|
12
tests/rustdoc-json/statics/statics.rs
Normal file
12
tests/rustdoc-json/statics/statics.rs
Normal file
@ -0,0 +1,12 @@
|
||||
//@ is '$.index[*][?(@.name=="A")].inner.static.type.primitive' '"i32"'
|
||||
//@ is '$.index[*][?(@.name=="A")].inner.static.is_mutable' false
|
||||
//@ is '$.index[*][?(@.name=="A")].inner.static.expr' '"5"'
|
||||
//@ is '$.index[*][?(@.name=="A")].inner.static.is_unsafe' false
|
||||
pub static A: i32 = 5;
|
||||
|
||||
//@ is '$.index[*][?(@.name=="B")].inner.static.type.primitive' '"u32"'
|
||||
//@ is '$.index[*][?(@.name=="B")].inner.static.is_mutable' true
|
||||
// Expr value isn't gaurenteed, it'd be fine to change it.
|
||||
//@ is '$.index[*][?(@.name=="B")].inner.static.expr' '"_"'
|
||||
//@ is '$.index[*][?(@.name=="B")].inner.static.is_unsafe' false
|
||||
pub static mut B: u32 = 2 + 3;
|
242
tests/ui-fulldeps/pprust-parenthesis-insertion.rs
Normal file
242
tests/ui-fulldeps/pprust-parenthesis-insertion.rs
Normal file
@ -0,0 +1,242 @@
|
||||
//@ run-pass
|
||||
//@ ignore-cross-compile
|
||||
|
||||
// This test covers the AST pretty-printer's automatic insertion of parentheses
|
||||
// into unparenthesized syntax trees according to precedence and various grammar
|
||||
// restrictions and edge cases.
|
||||
//
|
||||
// For example if the following syntax tree represents the expression a*(b+c),
|
||||
// in which the parenthesis is necessary for precedence:
|
||||
//
|
||||
// Binary('*', Path("a"), Paren(Binary('+', Path("b"), Path("c"))))
|
||||
//
|
||||
// then the pretty-printer needs to be able to print the following
|
||||
// unparenthesized syntax tree with an automatically inserted parenthesization.
|
||||
//
|
||||
// Binary('*', Path("a"), Binary('+', Path("b"), Path("c")))
|
||||
//
|
||||
// Handling this correctly is relevant in real-world code when pretty-printing
|
||||
// macro-generated syntax trees, in which expressions can get interpolated into
|
||||
// one another without any parenthesization being visible in the syntax tree.
|
||||
//
|
||||
// macro_rules! repro {
|
||||
// ($rhs:expr) => {
|
||||
// a * $rhs
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// let _ = repro!(b + c);
|
||||
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_ast_pretty;
|
||||
extern crate rustc_driver;
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_parse;
|
||||
extern crate rustc_session;
|
||||
extern crate rustc_span;
|
||||
extern crate smallvec;
|
||||
|
||||
use std::mem;
|
||||
use std::process::ExitCode;
|
||||
|
||||
use rustc_ast::ast::{DUMMY_NODE_ID, Expr, ExprKind, Stmt};
|
||||
use rustc_ast::mut_visit::{self, DummyAstNode as _, MutVisitor};
|
||||
use rustc_ast::node_id::NodeId;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::Diag;
|
||||
use rustc_parse::parser::Recovery;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::{DUMMY_SP, FileName, Span};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
// Every parenthesis in the following expressions is re-inserted by the
|
||||
// pretty-printer.
|
||||
//
|
||||
// FIXME: Some of them shouldn't be.
|
||||
static EXPRS: &[&str] = &[
|
||||
// Straightforward binary operator precedence.
|
||||
"2 * 2 + 2",
|
||||
"2 + 2 * 2",
|
||||
"(2 + 2) * 2",
|
||||
"2 * (2 + 2)",
|
||||
"2 + 2 + 2",
|
||||
// Return has lower precedence than a binary operator.
|
||||
"(return 2) + 2",
|
||||
"2 + (return 2)", // FIXME: no parenthesis needed.
|
||||
"(return) + 2", // FIXME: no parenthesis needed.
|
||||
// These mean different things.
|
||||
"return - 2",
|
||||
"(return) - 2",
|
||||
// These mean different things.
|
||||
"if let _ = true && false {}",
|
||||
"if let _ = (true && false) {}",
|
||||
// Conditions end at the first curly brace, so struct expressions need to be
|
||||
// parenthesized. Except in a match guard, where conditions end at arrow.
|
||||
"if let _ = (Struct {}) {}",
|
||||
"match 2 { _ if let _ = Struct {} => {} }",
|
||||
// Match arms terminate eagerly, so parenthesization is needed around some
|
||||
// expressions.
|
||||
"match 2 { _ => 1 - 1 }",
|
||||
"match 2 { _ => ({ 1 }) - 1 }",
|
||||
// Grammar restriction: break value starting with a labeled loop is not
|
||||
// allowed, except if the break is also labeled.
|
||||
"break 'outer 'inner: loop {} + 2",
|
||||
"break ('inner: loop {} + 2)",
|
||||
// Grammar restriction: the value in let-else is not allowed to end in a
|
||||
// curly brace.
|
||||
"{ let _ = 1 + 1 else {}; }",
|
||||
"{ let _ = (loop {}) else {}; }",
|
||||
"{ let _ = mac!() else {}; }",
|
||||
"{ let _ = (mac! {}) else {}; }",
|
||||
// Parentheses are necessary to prevent an eager statement boundary.
|
||||
"{ 2 - 1 }",
|
||||
"{ (match 2 {}) - 1 }",
|
||||
"{ (match 2 {})() - 1 }",
|
||||
"{ (match 2 {})[0] - 1 }",
|
||||
"{ (loop {}) - 1 }",
|
||||
// Angle bracket is eagerly parsed as a path's generic argument list.
|
||||
"(2 as T) < U",
|
||||
"(2 as T<U>) < V", // FIXME: no parentheses needed.
|
||||
/*
|
||||
// FIXME: pretty-printer produces invalid syntax. `2 + 2 as T < U`
|
||||
"(2 + 2 as T) < U",
|
||||
*/
|
||||
/*
|
||||
// FIXME: pretty-printer produces invalid syntax. `if (let _ = () && Struct {}.x) {}`
|
||||
"if let _ = () && (Struct {}).x {}",
|
||||
*/
|
||||
/*
|
||||
// FIXME: pretty-printer produces invalid syntax. `(1 < 2 == false) as usize`
|
||||
"((1 < 2) == false) as usize",
|
||||
*/
|
||||
/*
|
||||
// FIXME: pretty-printer produces invalid syntax. `for _ in 1..{ 2 } {}`
|
||||
"for _ in (1..{ 2 }) {}",
|
||||
*/
|
||||
/*
|
||||
// FIXME: pretty-printer loses the attribute. `{ let Struct { field } = s; }`
|
||||
"{ let Struct { #[attr] field } = s; }",
|
||||
*/
|
||||
/*
|
||||
// FIXME: pretty-printer turns this into a range. `0..to_string()`
|
||||
"(0.).to_string()",
|
||||
"0. .. 1.",
|
||||
*/
|
||||
/*
|
||||
// FIXME: pretty-printer loses the dyn*. `i as Trait`
|
||||
"i as dyn* Trait",
|
||||
*/
|
||||
];
|
||||
|
||||
// Flatten the content of parenthesis nodes into their parent node. For example
|
||||
// this syntax tree representing the expression a*(b+c):
|
||||
//
|
||||
// Binary('*', Path("a"), Paren(Binary('+', Path("b"), Path("c"))))
|
||||
//
|
||||
// would unparenthesize to:
|
||||
//
|
||||
// Binary('*', Path("a"), Binary('+', Path("b"), Path("c")))
|
||||
struct Unparenthesize;
|
||||
|
||||
impl MutVisitor for Unparenthesize {
|
||||
fn visit_expr(&mut self, e: &mut P<Expr>) {
|
||||
while let ExprKind::Paren(paren) = &mut e.kind {
|
||||
**e = mem::replace(&mut *paren, Expr::dummy());
|
||||
}
|
||||
mut_visit::walk_expr(self, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Erase Span information that could distinguish between identical expressions
|
||||
// parsed from different source strings.
|
||||
struct Normalize;
|
||||
|
||||
impl MutVisitor for Normalize {
|
||||
const VISIT_TOKENS: bool = true;
|
||||
|
||||
fn visit_id(&mut self, id: &mut NodeId) {
|
||||
*id = DUMMY_NODE_ID;
|
||||
}
|
||||
|
||||
fn visit_span(&mut self, span: &mut Span) {
|
||||
*span = DUMMY_SP;
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &mut P<Expr>) {
|
||||
if let ExprKind::Binary(binop, _left, _right) = &mut expr.kind {
|
||||
self.visit_span(&mut binop.span);
|
||||
}
|
||||
mut_visit::walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn flat_map_stmt(&mut self, mut stmt: Stmt) -> SmallVec<[Stmt; 1]> {
|
||||
self.visit_span(&mut stmt.span);
|
||||
mut_visit::walk_flat_map_stmt(self, stmt)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_expr(psess: &ParseSess, source_code: &str) -> Option<P<Expr>> {
|
||||
let parser = rustc_parse::unwrap_or_emit_fatal(rustc_parse::new_parser_from_source_str(
|
||||
psess,
|
||||
FileName::anon_source_code(source_code),
|
||||
source_code.to_owned(),
|
||||
));
|
||||
|
||||
let mut expr = parser.recovery(Recovery::Forbidden).parse_expr().map_err(Diag::cancel).ok()?;
|
||||
Normalize.visit_expr(&mut expr);
|
||||
Some(expr)
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let mut status = ExitCode::SUCCESS;
|
||||
let mut fail = |description: &str, before: &str, after: &str| {
|
||||
status = ExitCode::FAILURE;
|
||||
eprint!(
|
||||
"{description}\n BEFORE: {before}\n AFTER: {after}\n\n",
|
||||
before = before.replace('\n', "\n "),
|
||||
after = after.replace('\n', "\n "),
|
||||
);
|
||||
};
|
||||
|
||||
rustc_span::create_default_session_globals_then(|| {
|
||||
let psess = &ParseSess::new(vec![rustc_parse::DEFAULT_LOCALE_RESOURCE]);
|
||||
|
||||
for &source_code in EXPRS {
|
||||
let expr = parse_expr(psess, source_code).unwrap();
|
||||
|
||||
// Check for FALSE POSITIVE: pretty-printer inserting parentheses where not needed.
|
||||
// Pseudocode:
|
||||
// assert(expr == parse(print(expr)))
|
||||
let printed = &pprust::expr_to_string(&expr);
|
||||
let Some(expr2) = parse_expr(psess, printed) else {
|
||||
fail("Pretty-printer produced invalid syntax", source_code, printed);
|
||||
continue;
|
||||
};
|
||||
if format!("{expr:#?}") != format!("{expr2:#?}") {
|
||||
fail("Pretty-printer inserted unnecessary parenthesis", source_code, printed);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for FALSE NEGATIVE: pretty-printer failing to place necessary parentheses.
|
||||
// Pseudocode:
|
||||
// assert(unparenthesize(expr) == unparenthesize(parse(print(unparenthesize(expr)))))
|
||||
let mut expr = expr;
|
||||
Unparenthesize.visit_expr(&mut expr);
|
||||
let printed = &pprust::expr_to_string(&expr);
|
||||
let Some(mut expr2) = parse_expr(psess, printed) else {
|
||||
fail("Pretty-printer with no parens produced invalid syntax", source_code, printed);
|
||||
continue;
|
||||
};
|
||||
Unparenthesize.visit_expr(&mut expr2);
|
||||
if format!("{expr:#?}") != format!("{expr2:#?}") {
|
||||
fail("Pretty-printer lost necessary parentheses", source_code, printed);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
status
|
||||
}
|
@ -6,9 +6,9 @@ This folder contains `rustc`'s
|
||||
## Test Directives (Headers)
|
||||
|
||||
Typically, a UI test will have some test directives / headers which are
|
||||
special comments that tell compiletest how to build and intepret a test.
|
||||
special comments that tell compiletest how to build and interpret a test.
|
||||
|
||||
As part of an on-going effort to rewrite compiletest
|
||||
As part of an ongoing effort to rewrite compiletest
|
||||
(see <https://github.com/rust-lang/compiler-team/issues/536>), a major
|
||||
change proposal to change legacy compiletest-style headers `// <directive>`
|
||||
to [`ui_test`](https://github.com/oli-obk/ui_test)-style headers
|
||||
@ -30,6 +30,6 @@ but in `ui_test` style, the header would be written as
|
||||
|
||||
compiletest is changed to accept only `//@` directives for UI tests
|
||||
(currently), and will reject and report an error if it encounters any
|
||||
comments `// <content>` that may be parsed as an legacy compiletest-style
|
||||
comments `// <content>` that may be parsed as a legacy compiletest-style
|
||||
test header. To fix this, you should migrate to the `ui_test`-style header
|
||||
`//@ <content>`.
|
||||
|
@ -996,7 +996,6 @@ warn_non_default_branch = true
|
||||
contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html"
|
||||
users_on_vacation = [
|
||||
"jyn514",
|
||||
"oli-obk",
|
||||
]
|
||||
|
||||
[assign.adhoc_groups]
|
||||
|
Loading…
Reference in New Issue
Block a user