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
54 changed files with 811 additions and 395 deletions
|
@ -39,9 +39,7 @@ pub use crate::format::*;
|
||||||
use crate::ptr::P;
|
use crate::ptr::P;
|
||||||
use crate::token::{self, CommentKind, Delimiter};
|
use crate::token::{self, CommentKind, Delimiter};
|
||||||
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
|
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
|
||||||
use crate::util::parser::{
|
use crate::util::parser::{AssocOp, ExprPrecedence};
|
||||||
AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_RANGE, PREC_UNAMBIGUOUS,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A "Label" is an identifier of some point in sources,
|
/// A "Label" is an identifier of some point in sources,
|
||||||
/// e.g. in the following code:
|
/// e.g. in the following code:
|
||||||
|
@ -1317,29 +1315,29 @@ impl Expr {
|
||||||
Some(P(Ty { kind, id: self.id, span: self.span, tokens: None }))
|
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 {
|
match self.kind {
|
||||||
ExprKind::Closure(..) => PREC_CLOSURE,
|
ExprKind::Closure(..) => ExprPrecedence::Closure,
|
||||||
|
|
||||||
ExprKind::Break(..)
|
ExprKind::Break(..)
|
||||||
| ExprKind::Continue(..)
|
| ExprKind::Continue(..)
|
||||||
| ExprKind::Ret(..)
|
| ExprKind::Ret(..)
|
||||||
| ExprKind::Yield(..)
|
| ExprKind::Yield(..)
|
||||||
| ExprKind::Yeet(..)
|
| ExprKind::Yeet(..)
|
||||||
| ExprKind::Become(..) => PREC_JUMP,
|
| ExprKind::Become(..) => ExprPrecedence::Jump,
|
||||||
|
|
||||||
// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
|
// `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
|
// 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
|
// ensures that `pprust` will add parentheses in the right places to get the desired
|
||||||
// parse.
|
// parse.
|
||||||
ExprKind::Range(..) => PREC_RANGE,
|
ExprKind::Range(..) => ExprPrecedence::Range,
|
||||||
|
|
||||||
// Binop-like expr kinds, handled by `AssocOp`.
|
// Binop-like expr kinds, handled by `AssocOp`.
|
||||||
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8,
|
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
|
||||||
ExprKind::Cast(..) => AssocOp::As.precedence() as i8,
|
ExprKind::Cast(..) => ExprPrecedence::Cast,
|
||||||
|
|
||||||
ExprKind::Assign(..) |
|
ExprKind::Assign(..) |
|
||||||
ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8,
|
ExprKind::AssignOp(..) => ExprPrecedence::Assign,
|
||||||
|
|
||||||
// Unary, prefix
|
// Unary, prefix
|
||||||
ExprKind::AddrOf(..)
|
ExprKind::AddrOf(..)
|
||||||
|
@ -1348,7 +1346,7 @@ impl Expr {
|
||||||
// need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
|
// 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.
|
// but we need to print `(let _ = a) < b` as-is with parens.
|
||||||
| ExprKind::Let(..)
|
| ExprKind::Let(..)
|
||||||
| ExprKind::Unary(..) => PREC_PREFIX,
|
| ExprKind::Unary(..) => ExprPrecedence::Prefix,
|
||||||
|
|
||||||
// Never need parens
|
// Never need parens
|
||||||
ExprKind::Array(_)
|
ExprKind::Array(_)
|
||||||
|
@ -1381,7 +1379,7 @@ impl Expr {
|
||||||
| ExprKind::Underscore
|
| ExprKind::Underscore
|
||||||
| ExprKind::While(..)
|
| ExprKind::While(..)
|
||||||
| ExprKind::Err(_)
|
| ExprKind::Err(_)
|
||||||
| ExprKind::Dummy => PREC_UNAMBIGUOUS,
|
| ExprKind::Dummy => ExprPrecedence::Unambiguous,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,21 +128,21 @@ impl AssocOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the precedence of this operator
|
/// Gets the precedence of this operator
|
||||||
pub fn precedence(&self) -> usize {
|
pub fn precedence(&self) -> ExprPrecedence {
|
||||||
use AssocOp::*;
|
use AssocOp::*;
|
||||||
match *self {
|
match *self {
|
||||||
As => 14,
|
As => ExprPrecedence::Cast,
|
||||||
Multiply | Divide | Modulus => 13,
|
Multiply | Divide | Modulus => ExprPrecedence::Product,
|
||||||
Add | Subtract => 12,
|
Add | Subtract => ExprPrecedence::Sum,
|
||||||
ShiftLeft | ShiftRight => 11,
|
ShiftLeft | ShiftRight => ExprPrecedence::Shift,
|
||||||
BitAnd => 10,
|
BitAnd => ExprPrecedence::BitAnd,
|
||||||
BitXor => 9,
|
BitXor => ExprPrecedence::BitXor,
|
||||||
BitOr => 8,
|
BitOr => ExprPrecedence::BitOr,
|
||||||
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
|
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare,
|
||||||
LAnd => 6,
|
LAnd => ExprPrecedence::LAnd,
|
||||||
LOr => 5,
|
LOr => ExprPrecedence::LOr,
|
||||||
DotDot | DotDotEq => 4,
|
DotDot | DotDotEq => ExprPrecedence::Range,
|
||||||
Assign | AssignOp(_) => 2,
|
Assign | AssignOp(_) => ExprPrecedence::Assign,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,17 +229,44 @@ impl AssocOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const PREC_CLOSURE: i8 = -40;
|
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||||
pub const PREC_JUMP: i8 = -30;
|
pub enum ExprPrecedence {
|
||||||
pub const PREC_RANGE: i8 = -10;
|
Closure,
|
||||||
// The range 2..=14 is reserved for AssocOp binary operator precedences.
|
// return, break, yield
|
||||||
pub const PREC_PREFIX: i8 = 50;
|
Jump,
|
||||||
pub const PREC_UNAMBIGUOUS: i8 = 60;
|
// = += -= *= /= %= &= |= ^= <<= >>=
|
||||||
pub const PREC_FORCE_PAREN: i8 = 100;
|
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`.
|
/// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`.
|
||||||
pub fn prec_let_scrutinee_needs_par() -> usize {
|
pub fn prec_let_scrutinee_needs_par() -> ExprPrecedence {
|
||||||
AssocOp::LAnd.precedence()
|
ExprPrecedence::LAnd
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Suppose we have `let _ = e` and the `order` of `e`.
|
/// 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`.
|
/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
|
||||||
/// Can we print this as `let _ = a OP b`?
|
/// Can we print this as `let _ = a OP b`?
|
||||||
pub fn needs_par_as_let_scrutinee(order: i8) -> bool {
|
pub fn needs_par_as_let_scrutinee(order: ExprPrecedence) -> bool {
|
||||||
order <= prec_let_scrutinee_needs_par() as i8
|
order <= prec_let_scrutinee_needs_par()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any
|
/// 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::ptr::P;
|
||||||
use rustc_ast::util::classify;
|
use rustc_ast::util::classify;
|
||||||
use rustc_ast::util::literal::escape_byte_str_symbol;
|
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::{
|
use rustc_ast::{
|
||||||
self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
|
self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
|
||||||
FormatDebugHex, FormatSign, FormatTrait, token,
|
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) {
|
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
|
||||||
let prec = match func.kind {
|
let needs_paren = match func.kind {
|
||||||
ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
|
ast::ExprKind::Field(..) => true,
|
||||||
_ => parser::PREC_UNAMBIGUOUS,
|
_ => func.precedence() < ExprPrecedence::Unambiguous,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Independent of parenthesization related to precedence, we must
|
// 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.
|
// because the latter is valid syntax but with the incorrect meaning.
|
||||||
// It's a match-expression followed by tuple-expression, not a function
|
// It's a match-expression followed by tuple-expression, not a function
|
||||||
// call.
|
// 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)
|
self.print_call_post(args)
|
||||||
}
|
}
|
||||||
|
@ -256,7 +256,7 @@ impl<'a> State<'a> {
|
||||||
// a statement containing an expression.
|
// a statement containing an expression.
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
receiver,
|
receiver,
|
||||||
receiver.precedence() < parser::PREC_UNAMBIGUOUS,
|
receiver.precedence() < ExprPrecedence::Unambiguous,
|
||||||
fixup,
|
fixup,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -276,21 +276,22 @@ impl<'a> State<'a> {
|
||||||
fixup: FixupContext,
|
fixup: FixupContext,
|
||||||
) {
|
) {
|
||||||
let assoc_op = AssocOp::from_ast_binop(op.node);
|
let assoc_op = AssocOp::from_ast_binop(op.node);
|
||||||
let prec = assoc_op.precedence() as i8;
|
let binop_prec = assoc_op.precedence();
|
||||||
let fixity = assoc_op.fixity();
|
let left_prec = lhs.precedence();
|
||||||
|
let right_prec = rhs.precedence();
|
||||||
|
|
||||||
let (left_prec, right_prec) = match fixity {
|
let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() {
|
||||||
Fixity::Left => (prec, prec + 1),
|
Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
|
||||||
Fixity::Right => (prec + 1, prec),
|
Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
|
||||||
Fixity::None => (prec + 1, prec + 1),
|
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
|
// 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
|
// 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.
|
// of `(x as i32) < ...`. We need to convince it _not_ to do that.
|
||||||
(&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => {
|
(&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => {
|
||||||
parser::PREC_FORCE_PAREN
|
left_needs_paren = true;
|
||||||
}
|
}
|
||||||
// We are given `(let _ = a) OP b`.
|
// 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,
|
// - 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
|
// parens are required since the parser would interpret `let a = b < c` as
|
||||||
// `let a = (b < c)`. To achieve this, we force parens.
|
// `let a = (b < c)`. To achieve this, we force parens.
|
||||||
(&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
|
(&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(binop_prec) => {
|
||||||
parser::PREC_FORCE_PAREN
|
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.space();
|
||||||
self.word_space(op.node.as_str());
|
self.word_space(op.node.as_str());
|
||||||
|
self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression());
|
||||||
self.print_expr_cond_paren(
|
|
||||||
rhs,
|
|
||||||
rhs.precedence() < right_prec,
|
|
||||||
fixup.subsequent_subexpression(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) {
|
fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) {
|
||||||
self.word(op.as_str());
|
self.word(op.as_str());
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
expr,
|
expr,
|
||||||
expr.precedence() < parser::PREC_PREFIX,
|
expr.precedence() < ExprPrecedence::Prefix,
|
||||||
fixup.subsequent_subexpression(),
|
fixup.subsequent_subexpression(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -348,7 +339,7 @@ impl<'a> State<'a> {
|
||||||
}
|
}
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
expr,
|
expr,
|
||||||
expr.precedence() < parser::PREC_PREFIX,
|
expr.precedence() < ExprPrecedence::Prefix,
|
||||||
fixup.subsequent_subexpression(),
|
fixup.subsequent_subexpression(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -432,10 +423,9 @@ impl<'a> State<'a> {
|
||||||
self.print_token_literal(lit, expr.span)
|
self.print_token_literal(lit, expr.span)
|
||||||
}
|
}
|
||||||
ast::ExprKind::Cast(expr, ty) => {
|
ast::ExprKind::Cast(expr, ty) => {
|
||||||
let prec = AssocOp::As.precedence() as i8;
|
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
expr,
|
expr,
|
||||||
expr.precedence() < prec,
|
expr.precedence() < ExprPrecedence::Cast,
|
||||||
fixup.leftmost_subexpression(),
|
fixup.leftmost_subexpression(),
|
||||||
);
|
);
|
||||||
self.space();
|
self.space();
|
||||||
|
@ -512,7 +502,7 @@ impl<'a> State<'a> {
|
||||||
MatchKind::Postfix => {
|
MatchKind::Postfix => {
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
expr,
|
expr,
|
||||||
expr.precedence() < parser::PREC_UNAMBIGUOUS,
|
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||||
fixup,
|
fixup,
|
||||||
);
|
);
|
||||||
self.word_nbsp(".match");
|
self.word_nbsp(".match");
|
||||||
|
@ -576,31 +566,31 @@ impl<'a> State<'a> {
|
||||||
ast::ExprKind::Await(expr, _) => {
|
ast::ExprKind::Await(expr, _) => {
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
expr,
|
expr,
|
||||||
expr.precedence() < parser::PREC_UNAMBIGUOUS,
|
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||||
fixup,
|
fixup,
|
||||||
);
|
);
|
||||||
self.word(".await");
|
self.word(".await");
|
||||||
}
|
}
|
||||||
ast::ExprKind::Assign(lhs, rhs, _) => {
|
ast::ExprKind::Assign(lhs, rhs, _) => {
|
||||||
let prec = AssocOp::Assign.precedence() as i8;
|
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
lhs,
|
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(),
|
fixup.leftmost_subexpression(),
|
||||||
);
|
);
|
||||||
self.space();
|
self.space();
|
||||||
self.word_space("=");
|
self.word_space("=");
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
rhs,
|
rhs,
|
||||||
rhs.precedence() < prec,
|
rhs.precedence() < ExprPrecedence::Assign,
|
||||||
fixup.subsequent_subexpression(),
|
fixup.subsequent_subexpression(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ast::ExprKind::AssignOp(op, lhs, rhs) => {
|
ast::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||||
let prec = AssocOp::Assign.precedence() as i8;
|
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
lhs,
|
lhs,
|
||||||
lhs.precedence() <= prec,
|
lhs.precedence() <= ExprPrecedence::Range,
|
||||||
fixup.leftmost_subexpression(),
|
fixup.leftmost_subexpression(),
|
||||||
);
|
);
|
||||||
self.space();
|
self.space();
|
||||||
|
@ -608,14 +598,14 @@ impl<'a> State<'a> {
|
||||||
self.word_space("=");
|
self.word_space("=");
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
rhs,
|
rhs,
|
||||||
rhs.precedence() < prec,
|
rhs.precedence() < ExprPrecedence::Assign,
|
||||||
fixup.subsequent_subexpression(),
|
fixup.subsequent_subexpression(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ast::ExprKind::Field(expr, ident) => {
|
ast::ExprKind::Field(expr, ident) => {
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
expr,
|
expr,
|
||||||
expr.precedence() < parser::PREC_UNAMBIGUOUS,
|
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||||
fixup,
|
fixup,
|
||||||
);
|
);
|
||||||
self.word(".");
|
self.word(".");
|
||||||
|
@ -624,7 +614,7 @@ impl<'a> State<'a> {
|
||||||
ast::ExprKind::Index(expr, index, _) => {
|
ast::ExprKind::Index(expr, index, _) => {
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
expr,
|
expr,
|
||||||
expr.precedence() < parser::PREC_UNAMBIGUOUS,
|
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||||
fixup.leftmost_subexpression(),
|
fixup.leftmost_subexpression(),
|
||||||
);
|
);
|
||||||
self.word("[");
|
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)`.
|
// 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
|
// 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.)
|
// 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 {
|
if let Some(e) = start {
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
e,
|
e,
|
||||||
|
@ -671,7 +661,7 @@ impl<'a> State<'a> {
|
||||||
expr,
|
expr,
|
||||||
// Parenthesize if required by precedence, or in the
|
// Parenthesize if required by precedence, or in the
|
||||||
// case of `break 'inner: loop { break 'inner 1 } + 1`
|
// 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)),
|
|| (opt_label.is_none() && classify::leading_labeled_expr(expr)),
|
||||||
fixup.subsequent_subexpression(),
|
fixup.subsequent_subexpression(),
|
||||||
);
|
);
|
||||||
|
@ -690,7 +680,7 @@ impl<'a> State<'a> {
|
||||||
self.word(" ");
|
self.word(" ");
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
expr,
|
expr,
|
||||||
expr.precedence() < parser::PREC_JUMP,
|
expr.precedence() < ExprPrecedence::Jump,
|
||||||
fixup.subsequent_subexpression(),
|
fixup.subsequent_subexpression(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -703,7 +693,7 @@ impl<'a> State<'a> {
|
||||||
self.word(" ");
|
self.word(" ");
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
expr,
|
expr,
|
||||||
expr.precedence() < parser::PREC_JUMP,
|
expr.precedence() < ExprPrecedence::Jump,
|
||||||
fixup.subsequent_subexpression(),
|
fixup.subsequent_subexpression(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -713,7 +703,7 @@ impl<'a> State<'a> {
|
||||||
self.word(" ");
|
self.word(" ");
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
result,
|
result,
|
||||||
result.precedence() < parser::PREC_JUMP,
|
result.precedence() < ExprPrecedence::Jump,
|
||||||
fixup.subsequent_subexpression(),
|
fixup.subsequent_subexpression(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -767,13 +757,13 @@ impl<'a> State<'a> {
|
||||||
self.space();
|
self.space();
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
expr,
|
expr,
|
||||||
expr.precedence() < parser::PREC_JUMP,
|
expr.precedence() < ExprPrecedence::Jump,
|
||||||
fixup.subsequent_subexpression(),
|
fixup.subsequent_subexpression(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprKind::Try(e) => {
|
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("?")
|
self.word("?")
|
||||||
}
|
}
|
||||||
ast::ExprKind::TryBlock(blk) => {
|
ast::ExprKind::TryBlock(blk) => {
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::path::{Path, PathBuf};
|
||||||
use std::process::ExitStatus;
|
use std::process::ExitStatus;
|
||||||
|
|
||||||
use rustc_abi::TargetDataLayoutErrors;
|
use rustc_abi::TargetDataLayoutErrors;
|
||||||
|
use rustc_ast::util::parser::ExprPrecedence;
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_macros::Subdiagnostic;
|
use rustc_macros::Subdiagnostic;
|
||||||
use rustc_span::Span;
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct DiagSymbolList<S = Symbol>(Vec<S>);
|
pub struct DiagSymbolList<S = Symbol>(Vec<S>);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use rustc_abi::ExternAbi;
|
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::{
|
use rustc_ast::{
|
||||||
self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label,
|
self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label,
|
||||||
LitKind, TraitObjectSyntax, UintTy,
|
LitKind, TraitObjectSyntax, UintTy,
|
||||||
|
@ -1695,22 +1695,22 @@ pub struct Expr<'hir> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expr<'_> {
|
impl Expr<'_> {
|
||||||
pub fn precedence(&self) -> i8 {
|
pub fn precedence(&self) -> ExprPrecedence {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ExprKind::Closure { .. } => PREC_CLOSURE,
|
ExprKind::Closure { .. } => ExprPrecedence::Closure,
|
||||||
|
|
||||||
ExprKind::Break(..)
|
ExprKind::Break(..)
|
||||||
| ExprKind::Continue(..)
|
| ExprKind::Continue(..)
|
||||||
| ExprKind::Ret(..)
|
| ExprKind::Ret(..)
|
||||||
| ExprKind::Yield(..)
|
| ExprKind::Yield(..)
|
||||||
| ExprKind::Become(..) => PREC_JUMP,
|
| ExprKind::Become(..) => ExprPrecedence::Jump,
|
||||||
|
|
||||||
// Binop-like expr kinds, handled by `AssocOp`.
|
// Binop-like expr kinds, handled by `AssocOp`.
|
||||||
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8,
|
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
|
||||||
ExprKind::Cast(..) => AssocOp::As.precedence() as i8,
|
ExprKind::Cast(..) => ExprPrecedence::Cast,
|
||||||
|
|
||||||
ExprKind::Assign(..) |
|
ExprKind::Assign(..) |
|
||||||
ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8,
|
ExprKind::AssignOp(..) => ExprPrecedence::Assign,
|
||||||
|
|
||||||
// Unary, prefix
|
// Unary, prefix
|
||||||
ExprKind::AddrOf(..)
|
ExprKind::AddrOf(..)
|
||||||
|
@ -1719,7 +1719,7 @@ impl Expr<'_> {
|
||||||
// need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
|
// 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.
|
// but we need to print `(let _ = a) < b` as-is with parens.
|
||||||
| ExprKind::Let(..)
|
| ExprKind::Let(..)
|
||||||
| ExprKind::Unary(..) => PREC_PREFIX,
|
| ExprKind::Unary(..) => ExprPrecedence::Prefix,
|
||||||
|
|
||||||
// Never need parens
|
// Never need parens
|
||||||
ExprKind::Array(_)
|
ExprKind::Array(_)
|
||||||
|
@ -1740,7 +1740,7 @@ impl Expr<'_> {
|
||||||
| ExprKind::Struct(..)
|
| ExprKind::Struct(..)
|
||||||
| ExprKind::Tup(_)
|
| ExprKind::Tup(_)
|
||||||
| ExprKind::Type(..)
|
| ExprKind::Type(..)
|
||||||
| ExprKind::Err(_) => PREC_UNAMBIGUOUS,
|
| ExprKind::Err(_) => ExprPrecedence::Unambiguous,
|
||||||
|
|
||||||
ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
|
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`,
|
// Fallback case: allow `ManuallyDrop` and things that are `Copy`,
|
||||||
// also no need to report an error if the type is unresolved.
|
// 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.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()
|
|| 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
|
// Check that the type implements Copy. The only case where this can
|
||||||
// possibly fail is for SIMD types which don't #[derive(Copy)].
|
// 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";
|
let msg = "arguments for inline assembly must be copyable";
|
||||||
self.tcx
|
self.tcx
|
||||||
.dcx()
|
.dcx()
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::cell::Cell;
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
use rustc_abi::ExternAbi;
|
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::Breaks::{Consistent, Inconsistent};
|
||||||
use rustc_ast_pretty::pp::{self, Breaks};
|
use rustc_ast_pretty::pp::{self, Breaks};
|
||||||
use rustc_ast_pretty::pprust::{Comments, PrintState};
|
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<'_>]) {
|
fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||||
let prec = match func.kind {
|
let needs_paren = match func.kind {
|
||||||
hir::ExprKind::Field(..) => parser::PREC_FORCE_PAREN,
|
hir::ExprKind::Field(..) => true,
|
||||||
_ => parser::PREC_UNAMBIGUOUS,
|
_ => 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)
|
self.print_call_post(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1141,7 +1141,7 @@ impl<'a> State<'a> {
|
||||||
args: &[hir::Expr<'_>],
|
args: &[hir::Expr<'_>],
|
||||||
) {
|
) {
|
||||||
let base_args = args;
|
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.word(".");
|
||||||
self.print_ident(segment.ident);
|
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<'_>) {
|
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 assoc_op = AssocOp::from_ast_binop(op.node);
|
||||||
let prec = assoc_op.precedence() as i8;
|
let binop_prec = assoc_op.precedence();
|
||||||
let fixity = assoc_op.fixity();
|
let left_prec = lhs.precedence();
|
||||||
|
let right_prec = rhs.precedence();
|
||||||
|
|
||||||
let (left_prec, right_prec) = match fixity {
|
let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() {
|
||||||
Fixity::Left => (prec, prec + 1),
|
Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
|
||||||
Fixity::Right => (prec + 1, prec),
|
Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
|
||||||
Fixity::None => (prec + 1, prec + 1),
|
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
|
// 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
|
// 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.
|
// of `(x as i32) < ...`. We need to convince it _not_ to do that.
|
||||||
(&hir::ExprKind::Cast { .. }, hir::BinOpKind::Lt | hir::BinOpKind::Shl) => {
|
(&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) => {
|
(&hir::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(binop_prec) => {
|
||||||
parser::PREC_FORCE_PAREN
|
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.space();
|
||||||
self.word_space(op.node.as_str());
|
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<'_>) {
|
fn print_expr_unary(&mut self, op: hir::UnOp, expr: &hir::Expr<'_>) {
|
||||||
self.word(op.as_str());
|
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(
|
fn print_expr_addr_of(
|
||||||
|
@ -1202,7 +1203,7 @@ impl<'a> State<'a> {
|
||||||
self.print_mutability(mutability, true);
|
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) {
|
fn print_literal(&mut self, lit: &hir::Lit) {
|
||||||
|
@ -1340,8 +1341,7 @@ impl<'a> State<'a> {
|
||||||
self.print_literal(lit);
|
self.print_literal(lit);
|
||||||
}
|
}
|
||||||
hir::ExprKind::Cast(expr, ty) => {
|
hir::ExprKind::Cast(expr, ty) => {
|
||||||
let prec = AssocOp::As.precedence() as i8;
|
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Cast);
|
||||||
self.print_expr_cond_paren(expr, expr.precedence() < prec);
|
|
||||||
self.space();
|
self.space();
|
||||||
self.word_space("as");
|
self.word_space("as");
|
||||||
self.print_type(ty);
|
self.print_type(ty);
|
||||||
|
@ -1442,27 +1442,25 @@ impl<'a> State<'a> {
|
||||||
self.print_block(blk);
|
self.print_block(blk);
|
||||||
}
|
}
|
||||||
hir::ExprKind::Assign(lhs, rhs, _) => {
|
hir::ExprKind::Assign(lhs, rhs, _) => {
|
||||||
let prec = AssocOp::Assign.precedence() as i8;
|
self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign);
|
||||||
self.print_expr_cond_paren(lhs, lhs.precedence() <= prec);
|
|
||||||
self.space();
|
self.space();
|
||||||
self.word_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) => {
|
hir::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||||
let prec = AssocOp::Assign.precedence() as i8;
|
self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign);
|
||||||
self.print_expr_cond_paren(lhs, lhs.precedence() <= prec);
|
|
||||||
self.space();
|
self.space();
|
||||||
self.word(op.node.as_str());
|
self.word(op.node.as_str());
|
||||||
self.word_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::Field(expr, ident) => {
|
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.word(".");
|
||||||
self.print_ident(ident);
|
self.print_ident(ident);
|
||||||
}
|
}
|
||||||
hir::ExprKind::Index(expr, index, _) => {
|
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.word("[");
|
||||||
self.print_expr(index);
|
self.print_expr(index);
|
||||||
self.word("]");
|
self.word("]");
|
||||||
|
@ -1476,7 +1474,7 @@ impl<'a> State<'a> {
|
||||||
}
|
}
|
||||||
if let Some(expr) = opt_expr {
|
if let Some(expr) = opt_expr {
|
||||||
self.space();
|
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) => {
|
hir::ExprKind::Continue(destination) => {
|
||||||
|
@ -1490,13 +1488,13 @@ impl<'a> State<'a> {
|
||||||
self.word("return");
|
self.word("return");
|
||||||
if let Some(expr) = result {
|
if let Some(expr) = result {
|
||||||
self.word(" ");
|
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) => {
|
hir::ExprKind::Become(result) => {
|
||||||
self.word("become");
|
self.word("become");
|
||||||
self.word(" ");
|
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) => {
|
hir::ExprKind::InlineAsm(asm) => {
|
||||||
self.word("asm!");
|
self.word("asm!");
|
||||||
|
@ -1521,7 +1519,7 @@ impl<'a> State<'a> {
|
||||||
}
|
}
|
||||||
hir::ExprKind::Yield(expr, _) => {
|
hir::ExprKind::Yield(expr, _) => {
|
||||||
self.word_space("yield");
|
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(_) => {
|
hir::ExprKind::Err(_) => {
|
||||||
self.popen();
|
self.popen();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::iter;
|
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_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
|
||||||
use rustc_hir::def::{self, CtorKind, Namespace, Res};
|
use rustc_hir::def::{self, CtorKind, Namespace, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
|
@ -606,7 +606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(rest_snippet) = rest_snippet {
|
if let Ok(rest_snippet) = rest_snippet {
|
||||||
let sugg = if callee_expr.precedence() >= PREC_UNAMBIGUOUS {
|
let sugg = if callee_expr.precedence() >= ExprPrecedence::Unambiguous {
|
||||||
vec![
|
vec![
|
||||||
(up_to_rcvr_span, "".to_string()),
|
(up_to_rcvr_span, "".to_string()),
|
||||||
(rest_span, format!(".{}({rest_snippet}", segment.ident)),
|
(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
|
//! expression, `e as U2` is not necessarily so (in fact it will only be valid if
|
||||||
//! `U1` coerces to `U2`).
|
//! `U1` coerces to `U2`).
|
||||||
|
|
||||||
|
use rustc_ast::util::parser::ExprPrecedence;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::codes::*;
|
use rustc_errors::codes::*;
|
||||||
use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
|
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) {
|
fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) {
|
||||||
let expr_prec = self.expr.precedence();
|
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 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);
|
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 {
|
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 {
|
fn body_owner_def_id(&self) -> LocalDefId {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use core::cmp::min;
|
||||||
use core::iter;
|
use core::iter;
|
||||||
|
|
||||||
use hir::def_id::LocalDefId;
|
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_data_structures::packed::Pu128;
|
||||||
use rustc_errors::{Applicability, Diag, MultiSpan};
|
use rustc_errors::{Applicability, Diag, MultiSpan};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
@ -398,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
// so we remove the user's `clone` call.
|
// so we remove the user's `clone` call.
|
||||||
{
|
{
|
||||||
vec![(receiver_method.ident.span, conversion_method.name.to_string())]
|
vec![(receiver_method.ident.span, conversion_method.name.to_string())]
|
||||||
} else if expr.precedence() < PREC_UNAMBIGUOUS {
|
} else if expr.precedence() < ExprPrecedence::Unambiguous {
|
||||||
vec![
|
vec![
|
||||||
(expr.span.shrink_to_lo(), "(".to_string()),
|
(expr.span.shrink_to_lo(), "(".to_string()),
|
||||||
(expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
|
(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 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())]
|
vec![(span.shrink_to_hi(), ".into()".to_owned())]
|
||||||
} else {
|
} else {
|
||||||
vec![
|
vec![
|
||||||
|
@ -3000,7 +3000,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
"change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
|
"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()));
|
sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
|
||||||
")"
|
")"
|
||||||
} else {
|
} else {
|
||||||
|
@ -3025,7 +3025,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let len = src.trim_end_matches(&checked_ty.to_string()).len();
|
let len = src.trim_end_matches(&checked_ty.to_string()).len();
|
||||||
expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
|
expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
|
||||||
},
|
},
|
||||||
if expr.precedence() < PREC_UNAMBIGUOUS {
|
if expr.precedence() < ExprPrecedence::Unambiguous {
|
||||||
// Readd `)`
|
// Readd `)`
|
||||||
format!("{expected_ty})")
|
format!("{expected_ty})")
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -586,7 +586,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()) {
|
if cx.type_is_copy_modulo_regions(ty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if type_implements_negative_copy_modulo_regions(cx.tcx, ty, cx.typing_env()) {
|
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 }
|
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,
|
/// Gets the type-checking results for the current body,
|
||||||
/// or `None` if outside a body.
|
/// or `None` if outside a body.
|
||||||
pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> {
|
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 Some(fn_name) = cx.tcx.get_diagnostic_name(def_id)
|
||||||
{
|
{
|
||||||
let arg_ty = cx.typeck_results().expr_ty(arg);
|
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 drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
|
||||||
let let_underscore_ignore_sugg = || {
|
let let_underscore_ignore_sugg = || {
|
||||||
if let Some((_, node)) = cx.tcx.hir().parent_iter(expr.hir_id).nth(0)
|
if let Some((_, node)) = cx.tcx.hir().parent_iter(expr.hir_id).nth(0)
|
||||||
|
|
|
@ -242,19 +242,12 @@ 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.
|
// Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait.
|
||||||
Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => {
|
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 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())
|
&& 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]));
|
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
|
/// 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,
|
/// not a structure at all. Corresponds to the only possible unsized field,
|
||||||
/// and its type can be used to determine unsizing strategy.
|
/// 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))
|
.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
|
/// Checks whether values of this type `T` have a size known at
|
||||||
/// compile time (i.e., whether `T: Sized`). Lifetimes are ignored
|
/// compile time (i.e., whether `T: Sized`). Lifetimes are ignored
|
||||||
/// for the purposes of this check, so it can be an
|
/// 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;
|
let ty = expr.ty;
|
||||||
if !ty.is_sized(tcx, this.typing_env()) {
|
if !ty.is_sized(tcx, this.typing_env()) {
|
||||||
// !sized means !copy, so this is an unsized move
|
// !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
|
// 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.
|
// 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;
|
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;
|
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
|
// Normally, this shouldn't be required, but trait normalization failure can create a
|
||||||
// validation ICE.
|
// validation ICE.
|
||||||
if !validate_types(
|
if !validate_types(self.tcx, self.typing_env, &callee_body, &caller_body).is_empty() {
|
||||||
self.tcx,
|
|
||||||
MirPhase::Runtime(RuntimePhase::Optimized),
|
|
||||||
self.typing_env,
|
|
||||||
&callee_body,
|
|
||||||
&caller_body,
|
|
||||||
)
|
|
||||||
.is_empty()
|
|
||||||
{
|
|
||||||
return Err("failed to validate callee body");
|
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) {
|
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) {
|
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 {
|
pub(super) struct Validator {
|
||||||
/// Describes at which point in the pipeline this validation is happening.
|
/// Describes at which point in the pipeline this validation is happening.
|
||||||
pub when: String,
|
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 {
|
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(..)) {
|
if matches!(body.source.instance, InstanceKind::Intrinsic(..) | InstanceKind::Virtual(..)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debug_assert_eq!(self.mir_phase, body.phase);
|
|
||||||
let def_id = body.source.def_id();
|
let def_id = body.source.def_id();
|
||||||
let mir_phase = body.phase;
|
|
||||||
let typing_env = body.typing_env(tcx);
|
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.
|
// In this case `AbortUnwindingCalls` haven't yet been executed.
|
||||||
true
|
true
|
||||||
} else if !tcx.def_kind(def_id).is_fn_like() {
|
} 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,
|
ty::Coroutine(..) => ExternAbi::Rust,
|
||||||
// No need to do MIR validation on error bodies
|
// No need to do MIR validation on error bodies
|
||||||
ty::Error(_) => return,
|
ty::Error(_) => return,
|
||||||
_ => {
|
_ => span_bug!(body.span, "unexpected body ty: {body_ty:?}"),
|
||||||
span_bug!(body.span, "unexpected body ty: {:?} phase {:?}", body_ty, mir_phase)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ty::layout::fn_can_unwind(tcx, Some(def_id), body_abi)
|
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,
|
when: &self.when,
|
||||||
body,
|
body,
|
||||||
tcx,
|
tcx,
|
||||||
mir_phase,
|
|
||||||
unwind_edge_count: 0,
|
unwind_edge_count: 0,
|
||||||
reachable_blocks: traversal::reachable_as_bitset(body),
|
reachable_blocks: traversal::reachable_as_bitset(body),
|
||||||
value_cache: FxHashSet::default(),
|
value_cache: FxHashSet::default(),
|
||||||
|
@ -86,7 +75,7 @@ impl<'tcx> crate::MirPass<'tcx> for Validator {
|
||||||
cfg_checker.check_cleanup_control_flow();
|
cfg_checker.check_cleanup_control_flow();
|
||||||
|
|
||||||
// Also run the TypeChecker.
|
// 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);
|
cfg_checker.fail(location, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +96,6 @@ struct CfgChecker<'a, 'tcx> {
|
||||||
when: &'a str,
|
when: &'a str,
|
||||||
body: &'a Body<'tcx>,
|
body: &'a Body<'tcx>,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
mir_phase: MirPhase,
|
|
||||||
unwind_edge_count: usize,
|
unwind_edge_count: usize,
|
||||||
reachable_blocks: BitSet<BasicBlock>,
|
reachable_blocks: BitSet<BasicBlock>,
|
||||||
value_cache: FxHashSet<u128>,
|
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) {
|
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||||
match &statement.kind {
|
match &statement.kind {
|
||||||
StatementKind::AscribeUserType(..) => {
|
StatementKind::AscribeUserType(..) => {
|
||||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
"`AscribeUserType` should have been removed after drop lowering phase",
|
"`AscribeUserType` should have been removed after drop lowering phase",
|
||||||
|
@ -302,7 +290,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::FakeRead(..) => {
|
StatementKind::FakeRead(..) => {
|
||||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
"`FakeRead` should have been removed after drop lowering phase",
|
"`FakeRead` should have been removed after drop lowering phase",
|
||||||
|
@ -310,17 +298,17 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::SetDiscriminant { .. } => {
|
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");
|
self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::Deinit(..) => {
|
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");
|
self.fail(location, "`Deinit`is not allowed until deaggregation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::Retag(kind, _) => {
|
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
|
// DropsLowered`. However, this causes ICEs with generation of drop shims, which
|
||||||
// seem to fail to set their `MirPhase` correctly.
|
// seem to fail to set their `MirPhase` correctly.
|
||||||
if matches!(kind, RetagKind::TwoPhase) {
|
if matches!(kind, RetagKind::TwoPhase) {
|
||||||
|
@ -328,7 +316,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::Coverage(kind) => {
|
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
|
&& let CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. } = kind
|
||||||
{
|
{
|
||||||
self.fail(
|
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
|
// the return edge from the call. FIXME(tmiasko): Since this is a strictly code
|
||||||
// generation concern, the code generation should be responsible for handling
|
// generation concern, the code generation should be responsible for handling
|
||||||
// it.
|
// 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.is_critical_call_edge(target, unwind)
|
||||||
{
|
{
|
||||||
self.fail(
|
self.fail(
|
||||||
|
@ -440,7 +428,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
|
||||||
if self.body.coroutine.is_none() {
|
if self.body.coroutine.is_none() {
|
||||||
self.fail(location, "`Yield` cannot appear outside coroutine bodies");
|
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.fail(location, "`Yield` should have been replaced by coroutine lowering");
|
||||||
}
|
}
|
||||||
self.check_edge(location, *resume, EdgeKind::Normal);
|
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 } => {
|
TerminatorKind::FalseEdge { real_target, imaginary_target } => {
|
||||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
"`FalseEdge` should have been removed after drop elaboration",
|
"`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);
|
self.check_edge(location, *imaginary_target, EdgeKind::Normal);
|
||||||
}
|
}
|
||||||
TerminatorKind::FalseUnwind { real_target, unwind } => {
|
TerminatorKind::FalseUnwind { real_target, unwind } => {
|
||||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
"`FalseUnwind` should have been removed after drop elaboration",
|
"`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() {
|
if self.body.coroutine.is_none() {
|
||||||
self.fail(location, "`CoroutineDrop` cannot appear outside coroutine bodies");
|
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(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
"`CoroutineDrop` should have been replaced by coroutine lowering",
|
"`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.
|
/// `optimized_mir` is available.
|
||||||
pub(super) fn validate_types<'tcx>(
|
pub(super) fn validate_types<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
mir_phase: MirPhase,
|
|
||||||
typing_env: ty::TypingEnv<'tcx>,
|
typing_env: ty::TypingEnv<'tcx>,
|
||||||
body: &Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
caller_body: &Body<'tcx>,
|
caller_body: &Body<'tcx>,
|
||||||
) -> Vec<(Location, String)> {
|
) -> Vec<(Location, String)> {
|
||||||
let mut type_checker =
|
let mut type_checker = TypeChecker { body, caller_body, tcx, typing_env, failures: Vec::new() };
|
||||||
TypeChecker { body, caller_body, tcx, typing_env, mir_phase, failures: Vec::new() };
|
|
||||||
type_checker.visit_body(body);
|
type_checker.visit_body(body);
|
||||||
type_checker.failures
|
type_checker.failures
|
||||||
}
|
}
|
||||||
|
@ -548,7 +534,6 @@ struct TypeChecker<'a, 'tcx> {
|
||||||
caller_body: &'a Body<'tcx>,
|
caller_body: &'a Body<'tcx>,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
typing_env: ty::TypingEnv<'tcx>,
|
typing_env: ty::TypingEnv<'tcx>,
|
||||||
mir_phase: MirPhase,
|
|
||||||
failures: Vec<(Location, String)>,
|
failures: Vec<(Location, String)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,7 +562,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
|
|
||||||
// After borrowck subtyping should be fully explicit via
|
// After borrowck subtyping should be fully explicit via
|
||||||
// `Subtype` projections.
|
// `Subtype` projections.
|
||||||
let variance = if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
let variance = if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||||
Variance::Invariant
|
Variance::Invariant
|
||||||
} else {
|
} else {
|
||||||
Variance::Covariant
|
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) {
|
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
|
||||||
// This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
|
// This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
|
||||||
if self.tcx.sess.opts.unstable_opts.validate_mir
|
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.
|
// `Operand::Copy` is only supposed to be used with `Copy` types.
|
||||||
if let Operand::Copy(place) = operand {
|
if let Operand::Copy(place) = operand {
|
||||||
let ty = place.ty(&self.body.local_decls, self.tcx).ty;
|
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}"));
|
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 {
|
match elem {
|
||||||
ProjectionElem::OpaqueCast(ty)
|
ProjectionElem::OpaqueCast(ty)
|
||||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) =>
|
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) =>
|
||||||
{
|
{
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
|
@ -656,7 +641,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProjectionElem::Deref
|
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;
|
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
|
// Set off any `bug!`s in the type computation code
|
||||||
let _ = place.ty(&self.body.local_decls, self.tcx);
|
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
|
&& place.projection.len() > 1
|
||||||
&& cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo)
|
&& cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo)
|
||||||
&& place.projection[1..].contains(&ProjectionElem::Deref)
|
&& place.projection[1..].contains(&ProjectionElem::Deref)
|
||||||
|
@ -974,7 +959,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AggregateKind::RawPtr(pointee_ty, mutability) => {
|
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
|
// 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
|
// time of writing it's only ever introduced from intrinsic lowering, so
|
||||||
// earlier things just `bug!` on it.
|
// earlier things just `bug!` on it.
|
||||||
|
@ -1016,7 +1001,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Rvalue::Ref(_, BorrowKind::Fake(_), _) => {
|
Rvalue::Ref(_, BorrowKind::Fake(_), _) => {
|
||||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
"`Assign` statement with a `Fake` borrow should have been removed in runtime MIR",
|
"`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 => {
|
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
|
// It would probably be fine to support this in earlier phases, but at
|
||||||
// the time of writing it's only ever introduced from intrinsic
|
// the time of writing it's only ever introduced from intrinsic
|
||||||
// lowering or other runtime-phase optimization passes, so earlier
|
// 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 {:?}",
|
"CastKind::{kind:?} output must be a raw const pointer, not {:?}",
|
||||||
ty::RawPtr(_, Mutability::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:?}"));
|
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 {:?}",
|
"CastKind::{kind:?} output must be a raw pointer, not {:?}",
|
||||||
ty::RawPtr(..)
|
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:?}"));
|
self.fail(location, format!("After borrowck, MIR disallows {kind:?}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1288,7 +1273,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CastKind::Transmute => {
|
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
|
// Unlike `mem::transmute`, a MIR `Transmute` is well-formed
|
||||||
// for any two `Sized` types, just potentially UB to run.
|
// for any two `Sized` types, just potentially UB to run.
|
||||||
|
|
||||||
|
@ -1317,7 +1302,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
location,
|
location,
|
||||||
format!(
|
format!(
|
||||||
"Transmute is not supported in non-runtime phase {:?}.",
|
"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(..) => {
|
StatementKind::AscribeUserType(..) => {
|
||||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
"`AscribeUserType` should have been removed after drop lowering phase",
|
"`AscribeUserType` should have been removed after drop lowering phase",
|
||||||
|
@ -1412,7 +1397,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::FakeRead(..) => {
|
StatementKind::FakeRead(..) => {
|
||||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
"`FakeRead` should have been removed after drop lowering phase",
|
"`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, .. } => {
|
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");
|
self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
|
||||||
}
|
}
|
||||||
let pty = place.ty(&self.body.local_decls, self.tcx).ty.kind();
|
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(..) => {
|
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");
|
self.fail(location, "`Deinit`is not allowed until deaggregation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::Retag(kind, _) => {
|
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
|
// DropsLowered`. However, this causes ICEs with generation of drop shims, which
|
||||||
// seem to fail to set their `MirPhase` correctly.
|
// seem to fail to set their `MirPhase` correctly.
|
||||||
if matches!(kind, RetagKind::TwoPhase) {
|
if matches!(kind, RetagKind::TwoPhase) {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use rustc_ast::token::Token;
|
use rustc_ast::token::Token;
|
||||||
|
use rustc_ast::util::parser::ExprPrecedence;
|
||||||
use rustc_ast::{Path, Visibility};
|
use rustc_ast::{Path, Visibility};
|
||||||
use rustc_errors::codes::*;
|
use rustc_errors::codes::*;
|
||||||
use rustc_errors::{
|
use rustc_errors::{
|
||||||
|
@ -2686,7 +2687,7 @@ pub(crate) struct UnexpectedExpressionInPattern {
|
||||||
/// Was a `RangePatternBound` expected?
|
/// Was a `RangePatternBound` expected?
|
||||||
pub is_bound: bool,
|
pub is_bound: bool,
|
||||||
/// The unexpected expr's precedence (used in match arm guard suggestions).
|
/// The unexpected expr's precedence (used in match arm guard suggestions).
|
||||||
pub expr_precedence: i8,
|
pub expr_precedence: ExprPrecedence,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// ignore-tidy-filelength
|
// ignore-tidy-filelength
|
||||||
|
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::ops::ControlFlow;
|
use core::ops::{Bound, ControlFlow};
|
||||||
|
|
||||||
use ast::mut_visit::{self, MutVisitor};
|
use ast::mut_visit::{self, MutVisitor};
|
||||||
use ast::token::IdentIsRaw;
|
use ast::token::IdentIsRaw;
|
||||||
|
@ -10,7 +10,7 @@ use rustc_ast::ptr::P;
|
||||||
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
|
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
|
||||||
use rustc_ast::util::case::Case;
|
use rustc_ast::util::case::Case;
|
||||||
use rustc_ast::util::classify;
|
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::visit::{Visitor, walk_expr};
|
||||||
use rustc_ast::{
|
use rustc_ast::{
|
||||||
self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy,
|
self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy,
|
||||||
|
@ -120,7 +120,7 @@ impl<'a> Parser<'a> {
|
||||||
r: Restrictions,
|
r: Restrictions,
|
||||||
attrs: AttrWrapper,
|
attrs: AttrWrapper,
|
||||||
) -> PResult<'a, (P<Expr>, bool)> {
|
) -> 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.
|
/// 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`).
|
/// followed by a subexpression (e.g. `1 + 2`).
|
||||||
pub(super) fn parse_expr_assoc_with(
|
pub(super) fn parse_expr_assoc_with(
|
||||||
&mut self,
|
&mut self,
|
||||||
min_prec: usize,
|
min_prec: Bound<ExprPrecedence>,
|
||||||
attrs: AttrWrapper,
|
attrs: AttrWrapper,
|
||||||
) -> PResult<'a, (P<Expr>, bool)> {
|
) -> PResult<'a, (P<Expr>, bool)> {
|
||||||
let lhs = if self.token.is_range_separator() {
|
let lhs = if self.token.is_range_separator() {
|
||||||
|
@ -144,7 +144,7 @@ impl<'a> Parser<'a> {
|
||||||
/// was actually parsed.
|
/// was actually parsed.
|
||||||
pub(super) fn parse_expr_assoc_rest_with(
|
pub(super) fn parse_expr_assoc_rest_with(
|
||||||
&mut self,
|
&mut self,
|
||||||
min_prec: usize,
|
min_prec: Bound<ExprPrecedence>,
|
||||||
starts_stmt: bool,
|
starts_stmt: bool,
|
||||||
mut lhs: P<Expr>,
|
mut lhs: P<Expr>,
|
||||||
) -> PResult<'a, (P<Expr>, bool)> {
|
) -> PResult<'a, (P<Expr>, bool)> {
|
||||||
|
@ -163,7 +163,11 @@ impl<'a> Parser<'a> {
|
||||||
self.restrictions
|
self.restrictions
|
||||||
};
|
};
|
||||||
let prec = op.node.precedence();
|
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;
|
break;
|
||||||
}
|
}
|
||||||
// Check for deprecated `...` syntax
|
// Check for deprecated `...` syntax
|
||||||
|
@ -276,16 +280,16 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let fixity = op.fixity();
|
let fixity = op.fixity();
|
||||||
let prec_adjustment = match fixity {
|
let min_prec = match fixity {
|
||||||
Fixity::Right => 0,
|
Fixity::Right => Bound::Included(prec),
|
||||||
Fixity::Left => 1,
|
Fixity::Left => Bound::Excluded(prec),
|
||||||
// We currently have no non-associative operators that are not handled above by
|
// We currently have no non-associative operators that are not handled above by
|
||||||
// the special cases. The code is here only for future convenience.
|
// 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 (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
|
||||||
let attrs = this.parse_outer_attributes()?;
|
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);
|
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.
|
/// The other two variants are handled in `parse_prefix_range_expr` below.
|
||||||
fn parse_expr_range(
|
fn parse_expr_range(
|
||||||
&mut self,
|
&mut self,
|
||||||
prec: usize,
|
prec: ExprPrecedence,
|
||||||
lhs: P<Expr>,
|
lhs: P<Expr>,
|
||||||
op: AssocOp,
|
op: AssocOp,
|
||||||
cur_op_span: Span,
|
cur_op_span: Span,
|
||||||
|
@ -460,7 +464,7 @@ impl<'a> Parser<'a> {
|
||||||
let maybe_lt = self.token.clone();
|
let maybe_lt = self.token.clone();
|
||||||
let attrs = self.parse_outer_attributes()?;
|
let attrs = self.parse_outer_attributes()?;
|
||||||
Some(
|
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))?
|
.map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?
|
||||||
.0,
|
.0,
|
||||||
)
|
)
|
||||||
|
@ -518,7 +522,7 @@ impl<'a> Parser<'a> {
|
||||||
let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() {
|
let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() {
|
||||||
// RHS must be parsed with more associativity than the dots.
|
// RHS must be parsed with more associativity than the dots.
|
||||||
let attrs = this.parse_outer_attributes()?;
|
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(|(x, _)| (lo.to(x.span), Some(x)))
|
||||||
.map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?
|
.map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?
|
||||||
} else {
|
} else {
|
||||||
|
@ -2643,7 +2647,8 @@ impl<'a> Parser<'a> {
|
||||||
self.expect(&token::Eq)?;
|
self.expect(&token::Eq)?;
|
||||||
}
|
}
|
||||||
let attrs = self.parse_outer_attributes()?;
|
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);
|
let span = lo.to(expr.span);
|
||||||
Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered)))
|
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::mut_visit::{self, MutVisitor};
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token};
|
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::visit::{self, Visitor};
|
||||||
use rustc_ast::{
|
use rustc_ast::{
|
||||||
self as ast, Arm, AttrVec, BinOpKind, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall,
|
self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability,
|
||||||
Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt,
|
Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind,
|
||||||
StmtKind,
|
|
||||||
};
|
};
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey};
|
use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey};
|
||||||
|
@ -435,8 +436,9 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
// Parse an associative expression such as `+ expr`, `% expr`, ...
|
// Parse an associative expression such as `+ expr`, `% expr`, ...
|
||||||
// Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
|
// Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
|
||||||
let Ok((expr, _)) =
|
let Ok((expr, _)) = snapshot
|
||||||
snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel())
|
.parse_expr_assoc_rest_with(Bound::Unbounded, false, expr)
|
||||||
|
.map_err(|err| err.cancel())
|
||||||
else {
|
else {
|
||||||
// We got a trailing method/operator, but that wasn't an expression.
|
// We got a trailing method/operator, but that wasn't an expression.
|
||||||
return None;
|
return None;
|
||||||
|
@ -545,10 +547,7 @@ impl<'a> Parser<'a> {
|
||||||
// HACK: a neater way would be preferable.
|
// HACK: a neater way would be preferable.
|
||||||
let expr = match &err.args["expr_precedence"] {
|
let expr = match &err.args["expr_precedence"] {
|
||||||
DiagArgValue::Number(expr_precedence) => {
|
DiagArgValue::Number(expr_precedence) => {
|
||||||
if *expr_precedence
|
if *expr_precedence <= ExprPrecedence::Compare as i32 {
|
||||||
<= AssocOp::from_ast_binop(BinOpKind::Eq).precedence()
|
|
||||||
as i32
|
|
||||||
{
|
|
||||||
format!("({expr})")
|
format!("({expr})")
|
||||||
} else {
|
} else {
|
||||||
format!("{expr}")
|
format!("{expr}")
|
||||||
|
@ -570,9 +569,7 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
Some(guard) => {
|
Some(guard) => {
|
||||||
// Are parentheses required around the old guard?
|
// Are parentheses required around the old guard?
|
||||||
let wrap_guard = guard.precedence()
|
let wrap_guard = guard.precedence() <= ExprPrecedence::LAnd;
|
||||||
<= AssocOp::from_ast_binop(BinOpKind::And).precedence()
|
|
||||||
as i8;
|
|
||||||
|
|
||||||
err.subdiagnostic(
|
err.subdiagnostic(
|
||||||
UnexpectedExpressionInPatternSugg::UpdateGuard {
|
UnexpectedExpressionInPatternSugg::UpdateGuard {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::ops::Bound;
|
||||||
|
|
||||||
use ast::Label;
|
use ast::Label;
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
|
@ -207,7 +208,7 @@ impl<'a> Parser<'a> {
|
||||||
// Perform this outside of the `collect_tokens` closure, since our
|
// Perform this outside of the `collect_tokens` closure, since our
|
||||||
// outer attributes do not apply to this part of the expression.
|
// outer attributes do not apply to this part of the expression.
|
||||||
let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| {
|
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)))
|
Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
|
||||||
} else {
|
} else {
|
||||||
|
@ -240,7 +241,7 @@ impl<'a> Parser<'a> {
|
||||||
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
|
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
|
||||||
let e = self.maybe_recover_from_bad_qpath(e)?;
|
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_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)
|
StmtKind::Expr(e)
|
||||||
};
|
};
|
||||||
Ok(self.mk_stmt(lo.to(hi), kind))
|
Ok(self.mk_stmt(lo.to(hi), kind))
|
||||||
|
|
|
@ -1358,6 +1358,12 @@ pub struct EarlyDiagCtxt {
|
||||||
dcx: DiagCtxt,
|
dcx: DiagCtxt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for EarlyDiagCtxt {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(ErrorOutputType::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl EarlyDiagCtxt {
|
impl EarlyDiagCtxt {
|
||||||
pub fn new(output: ErrorOutputType) -> Self {
|
pub fn new(output: ErrorOutputType) -> Self {
|
||||||
let emitter = mk_emitter(output);
|
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
|
// FIXME(#132279): This should be removed as it causes us to incorrectly
|
||||||
// handle opaques in their defining scope.
|
// handle opaques in their defining scope.
|
||||||
if !(param_env, ty).has_infer() {
|
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);
|
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) => {
|
ty::Closure(_, args) => {
|
||||||
for upvar in args.as_closure().upvar_tys() {
|
for upvar in args.as_closure().upvar_tys() {
|
||||||
|
|
|
@ -858,7 +858,7 @@ impl<T> [T] {
|
||||||
|
|
||||||
/// Gets a reference to the underlying array.
|
/// 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")]
|
#[unstable(feature = "slice_as_array", issue = "133508")]
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
|
@ -32,12 +32,11 @@ use std::iter::Peekable;
|
||||||
use std::ops::{ControlFlow, Range};
|
use std::ops::{ControlFlow, Range};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::{self, CharIndices};
|
use std::str::{self, CharIndices};
|
||||||
use std::sync::OnceLock;
|
|
||||||
|
|
||||||
use pulldown_cmark::{
|
use pulldown_cmark::{
|
||||||
BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html,
|
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_errors::{Diag, DiagMessage};
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_middle::ty::TyCtxt;
|
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)]
|
#[derive(Clone, Default, Debug)]
|
||||||
pub struct IdMap {
|
pub struct IdMap {
|
||||||
map: FxHashMap<Cow<'static, str>, usize>,
|
map: FxHashMap<String, usize>,
|
||||||
existing_footnotes: usize,
|
existing_footnotes: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The map is pre-initialized and then can be used as is to prevent cloning it for each item
|
fn is_default_id(id: &str) -> bool {
|
||||||
// (in the sidebar rendering).
|
matches!(
|
||||||
static DEFAULT_ID_MAP: OnceLock<FxHashSet<&'static str>> = OnceLock::new();
|
id,
|
||||||
|
|
||||||
fn init_id_map() -> FxHashSet<&'static str> {
|
|
||||||
let mut map = FxHashSet::default();
|
|
||||||
// This is the list of IDs used in JavaScript.
|
// This is the list of IDs used in JavaScript.
|
||||||
map.insert("help");
|
"help"
|
||||||
map.insert("settings");
|
| "settings"
|
||||||
map.insert("not-displayed");
|
| "not-displayed"
|
||||||
map.insert("alternative-display");
|
| "alternative-display"
|
||||||
map.insert("search");
|
| "search"
|
||||||
map.insert("crate-search");
|
| "crate-search"
|
||||||
map.insert("crate-search-div");
|
| "crate-search-div"
|
||||||
// This is the list of IDs used in HTML generated in Rust (including the ones
|
// This is the list of IDs used in HTML generated in Rust (including the ones
|
||||||
// used in tera template files).
|
// used in tera template files).
|
||||||
map.insert("themeStyle");
|
| "themeStyle"
|
||||||
map.insert("settings-menu");
|
| "settings-menu"
|
||||||
map.insert("help-button");
|
| "help-button"
|
||||||
map.insert("sidebar-button");
|
| "sidebar-button"
|
||||||
map.insert("main-content");
|
| "main-content"
|
||||||
map.insert("toggle-all-docs");
|
| "toggle-all-docs"
|
||||||
map.insert("all-types");
|
| "all-types"
|
||||||
map.insert("default-settings");
|
| "default-settings"
|
||||||
map.insert("sidebar-vars");
|
| "sidebar-vars"
|
||||||
map.insert("copy-path");
|
| "copy-path"
|
||||||
map.insert("rustdoc-toc");
|
| "rustdoc-toc"
|
||||||
map.insert("rustdoc-modnav");
|
| "rustdoc-modnav"
|
||||||
// This is the list of IDs used by rustdoc sections (but still generated by
|
// This is the list of IDs used by rustdoc sections (but still generated by
|
||||||
// rustdoc).
|
// rustdoc).
|
||||||
map.insert("fields");
|
| "fields"
|
||||||
map.insert("variants");
|
| "variants"
|
||||||
map.insert("implementors-list");
|
| "implementors-list"
|
||||||
map.insert("synthetic-implementors-list");
|
| "synthetic-implementors-list"
|
||||||
map.insert("foreign-impls");
|
| "foreign-impls"
|
||||||
map.insert("implementations");
|
| "implementations"
|
||||||
map.insert("trait-implementations");
|
| "trait-implementations"
|
||||||
map.insert("synthetic-implementations");
|
| "synthetic-implementations"
|
||||||
map.insert("blanket-implementations");
|
| "blanket-implementations"
|
||||||
map.insert("required-associated-types");
|
| "required-associated-types"
|
||||||
map.insert("provided-associated-types");
|
| "provided-associated-types"
|
||||||
map.insert("provided-associated-consts");
|
| "provided-associated-consts"
|
||||||
map.insert("required-associated-consts");
|
| "required-associated-consts"
|
||||||
map.insert("required-methods");
|
| "required-methods"
|
||||||
map.insert("provided-methods");
|
| "provided-methods"
|
||||||
map.insert("dyn-compatibility");
|
| "dyn-compatibility"
|
||||||
map.insert("implementors");
|
| "implementors"
|
||||||
map.insert("synthetic-implementors");
|
| "synthetic-implementors"
|
||||||
map.insert("implementations-list");
|
| "implementations-list"
|
||||||
map.insert("trait-implementations-list");
|
| "trait-implementations-list"
|
||||||
map.insert("synthetic-implementations-list");
|
| "synthetic-implementations-list"
|
||||||
map.insert("blanket-implementations-list");
|
| "blanket-implementations-list"
|
||||||
map.insert("deref-methods");
|
| "deref-methods"
|
||||||
map.insert("layout");
|
| "layout"
|
||||||
map.insert("aliased-type");
|
| "aliased-type"
|
||||||
map
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdMap {
|
impl IdMap {
|
||||||
|
@ -1953,7 +1949,7 @@ impl IdMap {
|
||||||
let id = match self.map.get_mut(candidate.as_ref()) {
|
let id = match self.map.get_mut(candidate.as_ref()) {
|
||||||
None => {
|
None => {
|
||||||
let candidate = candidate.to_string();
|
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);
|
let id = format!("{}-{}", candidate, 1);
|
||||||
self.map.insert(candidate.into(), 2);
|
self.map.insert(candidate.into(), 2);
|
||||||
id
|
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)),
|
MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), renderer)),
|
||||||
TyMethodItem(m) => ItemEnum::Function(from_function(m, false, header.unwrap(), renderer)),
|
TyMethodItem(m) => ItemEnum::Function(from_function(m, false, header.unwrap(), renderer)),
|
||||||
ImplItem(i) => ItemEnum::Impl((*i).into_json(renderer)),
|
ImplItem(i) => ItemEnum::Impl((*i).into_json(renderer)),
|
||||||
StaticItem(s) => ItemEnum::Static(s.into_json(renderer)),
|
StaticItem(s) => ItemEnum::Static(convert_static(s, rustc_hir::Safety::Safe, renderer)),
|
||||||
ForeignStaticItem(s, _) => ItemEnum::Static(s.into_json(renderer)),
|
ForeignStaticItem(s, safety) => ItemEnum::Static(convert_static(s, safety, renderer)),
|
||||||
ForeignTypeItem => ItemEnum::ExternType,
|
ForeignTypeItem => ItemEnum::ExternType,
|
||||||
TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_json(renderer)),
|
TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_json(renderer)),
|
||||||
// FIXME(generic_const_items): Add support for generic free consts
|
// FIXME(generic_const_items): Add support for generic free consts
|
||||||
|
@ -831,19 +831,22 @@ impl FromClean<Box<clean::TypeAlias>> for TypeAlias {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromClean<clean::Static> for Static {
|
fn convert_static(
|
||||||
fn from_clean(stat: clean::Static, renderer: &JsonRenderer<'_>) -> Self {
|
stat: clean::Static,
|
||||||
|
safety: rustc_hir::Safety,
|
||||||
|
renderer: &JsonRenderer<'_>,
|
||||||
|
) -> Static {
|
||||||
let tcx = renderer.tcx;
|
let tcx = renderer.tcx;
|
||||||
Static {
|
Static {
|
||||||
type_: (*stat.type_).into_json(renderer),
|
type_: (*stat.type_).into_json(renderer),
|
||||||
is_mutable: stat.mutability == ast::Mutability::Mut,
|
is_mutable: stat.mutability == ast::Mutability::Mut,
|
||||||
|
is_unsafe: safety == rustc_hir::Safety::Unsafe,
|
||||||
expr: stat
|
expr: stat
|
||||||
.expr
|
.expr
|
||||||
.map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e)))
|
.map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e)))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl FromClean<clean::TraitAlias> for TraitAlias {
|
impl FromClean<clean::TraitAlias> for TraitAlias {
|
||||||
fn from_clean(alias: clean::TraitAlias, renderer: &JsonRenderer<'_>) -> Self {
|
fn from_clean(alias: clean::TraitAlias, renderer: &JsonRenderer<'_>) -> Self {
|
||||||
|
|
|
@ -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,
|
/// This integer is incremented with every breaking change to the API,
|
||||||
/// and is returned along with the JSON blob as [`Crate::format_version`].
|
/// 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.
|
/// 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.
|
/// 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.
|
/// It's not guaranteed that it'll match the actual source code for the initial value.
|
||||||
pub expr: String,
|
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.
|
/// 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,
|
peel_middle_ty_refs,
|
||||||
};
|
};
|
||||||
use core::mem;
|
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_data_structures::fx::FxIndexMap;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def_id::DefId;
|
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_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.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})"))
|
Cow::Owned(format!("({expr_str})"))
|
||||||
} else {
|
} else {
|
||||||
expr_str
|
expr_str
|
||||||
|
@ -999,13 +999,13 @@ fn report<'tcx>(
|
||||||
data.first_expr.span,
|
data.first_expr.span,
|
||||||
state.msg,
|
state.msg,
|
||||||
|diag| {
|
|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 {
|
Node::Expr(e) => match e.kind {
|
||||||
ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false),
|
ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => false,
|
||||||
ExprKind::Call(..) => (PREC_UNAMBIGUOUS, matches!(expr.kind, ExprKind::Field(..))),
|
ExprKind::Call(..) => expr.precedence() < ExprPrecedence::Unambiguous || matches!(expr.kind, ExprKind::Field(..)),
|
||||||
_ => (e.precedence(), false),
|
_ => expr.precedence() < e.precedence(),
|
||||||
},
|
},
|
||||||
_ => (0, false),
|
_ => false,
|
||||||
};
|
};
|
||||||
let is_in_tuple = matches!(
|
let is_in_tuple = matches!(
|
||||||
get_parent_expr(cx, data.first_expr),
|
get_parent_expr(cx, data.first_expr),
|
||||||
|
@ -1016,7 +1016,7 @@ fn report<'tcx>(
|
||||||
);
|
);
|
||||||
|
|
||||||
let sugg = if !snip_is_macro
|
let sugg = if !snip_is_macro
|
||||||
&& (calls_field || expr.precedence() < precedence)
|
&& needs_paren
|
||||||
&& !has_enclosing_paren(&snip)
|
&& !has_enclosing_paren(&snip)
|
||||||
&& !is_in_tuple
|
&& !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() => {
|
Some(mutability) if !ty.is_ref() => {
|
||||||
let prefix = match mutability {
|
let prefix = match mutability {
|
||||||
Mutability::Not => "&",
|
Mutability::Not => "&",
|
||||||
Mutability::Mut => "&mut ",
|
Mutability::Mut => "&mut ",
|
||||||
};
|
};
|
||||||
(prefix, PREC_PREFIX)
|
(prefix, expr.precedence() < ExprPrecedence::Prefix)
|
||||||
},
|
},
|
||||||
None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", 0),
|
None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", false),
|
||||||
_ => ("", 0),
|
_ => ("", false),
|
||||||
};
|
};
|
||||||
span_lint_hir_and_then(
|
span_lint_hir_and_then(
|
||||||
cx,
|
cx,
|
||||||
|
@ -1070,7 +1070,7 @@ fn report<'tcx>(
|
||||||
let mut app = Applicability::MachineApplicable;
|
let mut app = Applicability::MachineApplicable;
|
||||||
let (snip, snip_is_macro) =
|
let (snip, snip_is_macro) =
|
||||||
snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
|
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})")
|
format!("{prefix}({snip})")
|
||||||
} else {
|
} else {
|
||||||
format!("{prefix}{snip}")
|
format!("{prefix}{snip}")
|
||||||
|
@ -1157,7 +1157,7 @@ impl<'tcx> Dereferencing<'tcx> {
|
||||||
},
|
},
|
||||||
Some(parent) if !parent.span.from_expansion() => {
|
Some(parent) if !parent.span.from_expansion() => {
|
||||||
// Double reference might be needed at this point.
|
// 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.
|
// Parentheses would be needed here, don't lint.
|
||||||
*outer_pat = None;
|
*outer_pat = None;
|
||||||
} else {
|
} 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::source::{indent_of, snippet, snippet_with_applicability};
|
||||||
use clippy_utils::visitors::contains_break_or_continue;
|
use clippy_utils::visitors::contains_break_or_continue;
|
||||||
use rustc_ast::Mutability;
|
use rustc_ast::Mutability;
|
||||||
use rustc_ast::util::parser::PREC_PREFIX;
|
use rustc_ast::util::parser::ExprPrecedence;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind, is_range_literal};
|
use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind, is_range_literal};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
@ -84,7 +84,7 @@ pub(super) fn check<'tcx>(
|
||||||
if !prefix.is_empty()
|
if !prefix.is_empty()
|
||||||
&& (
|
&& (
|
||||||
// Precedence of internal expression is less than or equal to precedence of `&expr`.
|
// 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();
|
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,
|
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,
|
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_errors::Applicability;
|
||||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
|
@ -117,7 +117,7 @@ where
|
||||||
// it's being passed by value.
|
// it's being passed by value.
|
||||||
let scrutinee = peel_hir_expr_refs(scrutinee).0;
|
let scrutinee = peel_hir_expr_refs(scrutinee).0;
|
||||||
let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
|
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})")
|
format!("({scrutinee_str})")
|
||||||
} else {
|
} else {
|
||||||
scrutinee_str.into()
|
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;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -158,7 +158,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
|
||||||
}
|
}
|
||||||
|
|
||||||
if can_lint
|
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.
|
// 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()))
|
&& (!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::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet_with_context;
|
use clippy_utils::source::snippet_with_context;
|
||||||
use clippy_utils::sugg::has_enclosing_paren;
|
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_errors::Applicability;
|
||||||
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
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 mut applicability = Applicability::MachineApplicable;
|
||||||
let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability);
|
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})")
|
format!("-({snip})")
|
||||||
} else {
|
} else {
|
||||||
format!("-{snip}")
|
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 mut applicability = Applicability::MachineApplicable;
|
||||||
let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
|
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(..));
|
&& !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
|
||||||
let sugg = if let Some(else_inner) = r#else {
|
let sugg = if let Some(else_inner) = r#else {
|
||||||
if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
|
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::source::snippet_with_context;
|
||||||
use clippy_utils::ty::is_type_lang_item;
|
use clippy_utils::ty::is_type_lang_item;
|
||||||
use clippy_utils::{get_parent_expr, peel_middle_ty_refs};
|
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_errors::Applicability;
|
||||||
use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
|
use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
|
||||||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
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 (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 (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed));
|
||||||
let parent_expr = get_parent_expr(cx, expr);
|
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_ty == indexed_ty {
|
||||||
if expr_ref_count > indexed_ref_count {
|
if expr_ref_count > indexed_ref_count {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
|
use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::sugg::Sugg;
|
use clippy_utils::sugg::Sugg;
|
||||||
use rustc_ast::util::parser::AssocOp;
|
use rustc_ast::util::parser::ExprPrecedence;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Expr, Node};
|
use rustc_hir::{Expr, Node};
|
||||||
use rustc_hir_typeck::cast::check_cast;
|
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)
|
if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id)
|
||||||
&& parent.precedence()
|
&& parent.precedence() > ExprPrecedence::Cast
|
||||||
> i8::try_from(AssocOp::As.precedence()).expect("AssocOp always returns a precedence < 128")
|
|
||||||
{
|
{
|
||||||
sugg = format!("({sugg})");
|
sugg = format!("({sugg})");
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub use type_certainty::expr_type_is_certain;
|
||||||
|
|
||||||
/// Checks if the given type implements copy.
|
/// Checks if the given type implements copy.
|
||||||
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
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.
|
/// 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::ffi::OsString;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TargetCfgs {
|
pub struct TargetCfgs {
|
||||||
pub current: TargetCfg,
|
pub current: TargetCfg,
|
||||||
|
@ -611,6 +614,17 @@ impl TargetCfgs {
|
||||||
("panic", Some("abort")) => cfg.panic = PanicStrategy::Abort,
|
("panic", Some("abort")) => cfg.panic = PanicStrategy::Abort,
|
||||||
("panic", Some("unwind")) => cfg.panic = PanicStrategy::Unwind,
|
("panic", Some("unwind")) => cfg.panic = PanicStrategy::Unwind,
|
||||||
("panic", other) => panic!("unexpected value for panic cfg: {other:?}"),
|
("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,
|
pub(crate) xray: bool,
|
||||||
#[serde(default = "default_reloc_model")]
|
#[serde(default = "default_reloc_model")]
|
||||||
pub(crate) relocation_model: String,
|
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 {
|
impl TargetCfg {
|
||||||
|
|
|
@ -17,7 +17,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
||||||
"check-run-results",
|
"check-run-results",
|
||||||
"check-stdout",
|
"check-stdout",
|
||||||
"check-test-line-numbers-match",
|
"check-test-line-numbers-match",
|
||||||
"compare-output-lines-by-subset",
|
|
||||||
"compile-flags",
|
"compile-flags",
|
||||||
"doc-flags",
|
"doc-flags",
|
||||||
"dont-check-compiler-stderr",
|
"dont-check-compiler-stderr",
|
||||||
|
@ -154,6 +153,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
||||||
"needs-sanitizer-thread",
|
"needs-sanitizer-thread",
|
||||||
"needs-std-debug-assertions",
|
"needs-std-debug-assertions",
|
||||||
"needs-symlink",
|
"needs-symlink",
|
||||||
|
"needs-target-has-atomic",
|
||||||
"needs-threads",
|
"needs-threads",
|
||||||
"needs-unwind",
|
"needs-unwind",
|
||||||
"needs-wasmtime",
|
"needs-wasmtime",
|
||||||
|
|
|
@ -115,9 +115,6 @@ pub struct TestProps {
|
||||||
pub dont_check_compiler_stdout: bool,
|
pub dont_check_compiler_stdout: bool,
|
||||||
// For UI tests, allows compiler to generate arbitrary output to stderr
|
// For UI tests, allows compiler to generate arbitrary output to stderr
|
||||||
pub dont_check_compiler_stderr: bool,
|
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
|
// 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
|
// 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 KNOWN_BUG: &'static str = "known-bug";
|
||||||
pub const TEST_MIR_PASS: &'static str = "test-mir-pass";
|
pub const TEST_MIR_PASS: &'static str = "test-mir-pass";
|
||||||
pub const REMAP_SRC_BASE: &'static str = "remap-src-base";
|
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 LLVM_COV_FLAGS: &'static str = "llvm-cov-flags";
|
||||||
pub const FILECHECK_FLAGS: &'static str = "filecheck-flags";
|
pub const FILECHECK_FLAGS: &'static str = "filecheck-flags";
|
||||||
pub const NO_AUTO_CHECK_CFG: &'static str = "no-auto-check-cfg";
|
pub const NO_AUTO_CHECK_CFG: &'static str = "no-auto-check-cfg";
|
||||||
|
@ -274,7 +270,6 @@ impl TestProps {
|
||||||
check_run_results: false,
|
check_run_results: false,
|
||||||
dont_check_compiler_stdout: false,
|
dont_check_compiler_stdout: false,
|
||||||
dont_check_compiler_stderr: false,
|
dont_check_compiler_stderr: false,
|
||||||
compare_output_lines_by_subset: false,
|
|
||||||
no_prefer_dynamic: false,
|
no_prefer_dynamic: false,
|
||||||
pretty_mode: "normal".to_string(),
|
pretty_mode: "normal".to_string(),
|
||||||
pretty_compare_only: false,
|
pretty_compare_only: false,
|
||||||
|
@ -550,11 +545,6 @@ impl TestProps {
|
||||||
|s| s.trim().to_string(),
|
|s| s.trim().to_string(),
|
||||||
);
|
);
|
||||||
config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base);
|
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) {
|
if let Some(flags) = config.parse_name_value_directive(ln, LLVM_COV_FLAGS) {
|
||||||
self.llvm_cov_flags.extend(split_flags(&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};
|
use crate::header::{IgnoreDecision, llvm_has_libzstd};
|
||||||
|
|
||||||
pub(super) fn handle_needs(
|
pub(super) fn handle_needs(
|
||||||
|
@ -171,11 +171,54 @@ pub(super) fn handle_needs(
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let (name, comment) = match ln.split_once([':', ' ']) {
|
let (name, rest) = match ln.split_once([':', ' ']) {
|
||||||
Some((name, comment)) => (name, Some(comment)),
|
Some((name, rest)) => (name, Some(rest)),
|
||||||
None => (ln, None),
|
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-") {
|
if !name.starts_with("needs-") {
|
||||||
return IgnoreDecision::Continue;
|
return IgnoreDecision::Continue;
|
||||||
}
|
}
|
||||||
|
@ -193,7 +236,7 @@ pub(super) fn handle_needs(
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
return IgnoreDecision::Ignore {
|
return IgnoreDecision::Ignore {
|
||||||
reason: if let Some(comment) = comment {
|
reason: if let Some(comment) = rest {
|
||||||
format!("{} ({})", need.ignore_reason, comment.trim())
|
format!("{} ({})", need.ignore_reason, comment.trim())
|
||||||
} else {
|
} else {
|
||||||
need.ignore_reason.into()
|
need.ignore_reason.into()
|
||||||
|
|
|
@ -787,3 +787,40 @@ fn test_not_trailing_directive() {
|
||||||
run_path(&mut poisoned, Path::new("a.rs"), b"//@ revisions: incremental");
|
run_path(&mut poisoned, Path::new("a.rs"), b"//@ revisions: incremental");
|
||||||
assert!(!poisoned);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If `compare-output-lines-by-subset` is not explicitly enabled then
|
// Wrapper tools set by `runner` might provide extra output on failure,
|
||||||
// auto-enable it when a `runner` is in use since wrapper tools might
|
// for example a WebAssembly runtime might print the stack trace of an
|
||||||
// provide extra output on failure, for example a WebAssembly runtime
|
// `unreachable` instruction by default.
|
||||||
// might print the stack trace of an `unreachable` instruction by
|
let compare_output_by_lines = self.config.runner.is_some();
|
||||||
// default.
|
|
||||||
let compare_output_by_lines =
|
|
||||||
self.props.compare_output_lines_by_subset || self.config.runner.is_some();
|
|
||||||
|
|
||||||
let tmp;
|
let tmp;
|
||||||
let (expected, actual): (&str, &str) = if compare_output_by_lines {
|
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)
|
## Test Directives (Headers)
|
||||||
|
|
||||||
Typically, a UI test will have some test directives / headers which are
|
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
|
(see <https://github.com/rust-lang/compiler-team/issues/536>), a major
|
||||||
change proposal to change legacy compiletest-style headers `// <directive>`
|
change proposal to change legacy compiletest-style headers `// <directive>`
|
||||||
to [`ui_test`](https://github.com/oli-obk/ui_test)-style headers
|
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
|
compiletest is changed to accept only `//@` directives for UI tests
|
||||||
(currently), and will reject and report an error if it encounters any
|
(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
|
test header. To fix this, you should migrate to the `ui_test`-style header
|
||||||
`//@ <content>`.
|
`//@ <content>`.
|
||||||
|
|
|
@ -996,7 +996,6 @@ warn_non_default_branch = true
|
||||||
contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html"
|
contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html"
|
||||||
users_on_vacation = [
|
users_on_vacation = [
|
||||||
"jyn514",
|
"jyn514",
|
||||||
"oli-obk",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[assign.adhoc_groups]
|
[assign.adhoc_groups]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue