Rollup of 9 pull requests Successful merges: - #122670 (Fix bug where `option_env!` would return `None` when env var is present but not valid Unicode) - #131095 (Use environment variables instead of command line arguments for merged doctests) - #131339 (Expand set_ptr_value / with_metadata_of docs) - #131652 (Move polarity into `PolyTraitRef` rather than storing it on the side) - #131675 (Update lint message for ABI not supported) - #131681 (Fix up-to-date checking for run-make tests) - #131702 (Suppress import errors for traits that couldve applied for method lookup error) - #131703 (Resolved python deprecation warning in publish_toolstate.py) - #131710 (Remove `'apostrophes'` from `rustc_parse_format`) r? `@ghost` `@rustbot` modify labels: rollup
313 lines
9.8 KiB
Rust
313 lines
9.8 KiB
Rust
//! Routines the parser and pretty-printer use to classify AST nodes.
|
|
|
|
use crate::ast::ExprKind::*;
|
|
use crate::ast::{self, MatchKind};
|
|
use crate::token::Delimiter;
|
|
|
|
/// This classification determines whether various syntactic positions break out
|
|
/// of parsing the current expression (true) or continue parsing more of the
|
|
/// same expression (false).
|
|
///
|
|
/// For example, it's relevant in the parsing of match arms:
|
|
///
|
|
/// ```ignore (illustrative)
|
|
/// match ... {
|
|
/// // Is this calling $e as a function, or is it the start of a new arm
|
|
/// // with a tuple pattern?
|
|
/// _ => $e (
|
|
/// ^ )
|
|
///
|
|
/// // Is this an Index operation, or new arm with a slice pattern?
|
|
/// _ => $e [
|
|
/// ^ ]
|
|
///
|
|
/// // Is this a binary operator, or leading vert in a new arm? Same for
|
|
/// // other punctuation which can either be a binary operator in
|
|
/// // expression or unary operator in pattern, such as `&` and `-`.
|
|
/// _ => $e |
|
|
/// ^
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// If $e is something like `{}` or `if … {}`, then terminate the current
|
|
/// arm and parse a new arm.
|
|
///
|
|
/// If $e is something like `path::to` or `(…)`, continue parsing the same
|
|
/// arm.
|
|
///
|
|
/// *Almost* the same classification is used as an early bail-out for parsing
|
|
/// statements. See `expr_requires_semi_to_be_stmt`.
|
|
pub fn expr_is_complete(e: &ast::Expr) -> bool {
|
|
matches!(
|
|
e.kind,
|
|
If(..)
|
|
| Match(..)
|
|
| Block(..)
|
|
| While(..)
|
|
| Loop(..)
|
|
| ForLoop { .. }
|
|
| TryBlock(..)
|
|
| ConstBlock(..)
|
|
)
|
|
}
|
|
|
|
/// Does this expression require a semicolon to be treated as a statement?
|
|
///
|
|
/// The negation of this: "can this expression be used as a statement without a
|
|
/// semicolon" -- is used as an early bail-out when parsing statements so that,
|
|
/// for instance,
|
|
///
|
|
/// ```ignore (illustrative)
|
|
/// if true {...} else {...}
|
|
/// |x| 5
|
|
/// ```
|
|
///
|
|
/// isn't parsed as `(if true {...} else {...} | x) | 5`.
|
|
///
|
|
/// Surprising special case: even though braced macro calls like `m! {}`
|
|
/// normally do not introduce a boundary when found at the head of a match arm,
|
|
/// they do terminate the parsing of a statement.
|
|
///
|
|
/// ```ignore (illustrative)
|
|
/// match ... {
|
|
/// _ => m! {} (), // macro that expands to a function, which is then called
|
|
/// }
|
|
///
|
|
/// let _ = { m! {} () }; // macro call followed by unit
|
|
/// ```
|
|
pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
|
|
match &e.kind {
|
|
MacCall(mac_call) => mac_call.args.delim != Delimiter::Brace,
|
|
_ => !expr_is_complete(e),
|
|
}
|
|
}
|
|
|
|
/// Returns whether the leftmost token of the given expression is the label of a
|
|
/// labeled loop or block, such as in `'inner: loop { break 'inner 1 } + 1`.
|
|
///
|
|
/// Such expressions are not allowed as the value of an unlabeled break.
|
|
///
|
|
/// ```ignore (illustrative)
|
|
/// 'outer: {
|
|
/// break 'inner: loop { break 'inner 1 } + 1; // invalid syntax
|
|
///
|
|
/// break 'outer 'inner: loop { break 'inner 1 } + 1; // okay
|
|
///
|
|
/// break ('inner: loop { break 'inner 1 } + 1); // okay
|
|
///
|
|
/// break ('inner: loop { break 'inner 1 }) + 1; // okay
|
|
/// }
|
|
/// ```
|
|
pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
|
|
loop {
|
|
match &expr.kind {
|
|
Block(_, label) | ForLoop { label, .. } | Loop(_, label, _) | While(_, _, label) => {
|
|
return label.is_some();
|
|
}
|
|
|
|
Assign(e, _, _)
|
|
| AssignOp(_, e, _)
|
|
| Await(e, _)
|
|
| Binary(_, e, _)
|
|
| Call(e, _)
|
|
| Cast(e, _)
|
|
| Field(e, _)
|
|
| Index(e, _, _)
|
|
| Match(e, _, MatchKind::Postfix)
|
|
| Range(Some(e), _, _)
|
|
| Try(e) => {
|
|
expr = e;
|
|
}
|
|
MethodCall(method_call) => {
|
|
expr = &method_call.receiver;
|
|
}
|
|
|
|
AddrOf(..)
|
|
| Array(..)
|
|
| Become(..)
|
|
| Break(..)
|
|
| Closure(..)
|
|
| ConstBlock(..)
|
|
| Continue(..)
|
|
| FormatArgs(..)
|
|
| Gen(..)
|
|
| If(..)
|
|
| IncludedBytes(..)
|
|
| InlineAsm(..)
|
|
| Let(..)
|
|
| Lit(..)
|
|
| MacCall(..)
|
|
| Match(_, _, MatchKind::Prefix)
|
|
| OffsetOf(..)
|
|
| Paren(..)
|
|
| Path(..)
|
|
| Range(None, _, _)
|
|
| Repeat(..)
|
|
| Ret(..)
|
|
| Struct(..)
|
|
| TryBlock(..)
|
|
| Tup(..)
|
|
| Type(..)
|
|
| Unary(..)
|
|
| Underscore
|
|
| Yeet(..)
|
|
| Yield(..)
|
|
| Err(..)
|
|
| Dummy => return false,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub enum TrailingBrace<'a> {
|
|
/// Trailing brace in a macro call, like the one in `x as *const brace! {}`.
|
|
/// We will suggest changing the macro call to a different delimiter.
|
|
MacCall(&'a ast::MacCall),
|
|
/// Trailing brace in any other expression, such as `a + B {}`. We will
|
|
/// suggest wrapping the innermost expression in parentheses: `a + (B {})`.
|
|
Expr(&'a ast::Expr),
|
|
}
|
|
|
|
/// If an expression ends with `}`, returns the innermost expression ending in the `}`
|
|
pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
|
|
loop {
|
|
match &expr.kind {
|
|
AddrOf(_, _, e)
|
|
| Assign(_, e, _)
|
|
| AssignOp(_, _, e)
|
|
| Binary(_, _, e)
|
|
| Break(_, Some(e))
|
|
| Let(_, e, _, _)
|
|
| Range(_, Some(e), _)
|
|
| Ret(Some(e))
|
|
| Unary(_, e)
|
|
| Yield(Some(e))
|
|
| Yeet(Some(e))
|
|
| Become(e) => {
|
|
expr = e;
|
|
}
|
|
Closure(closure) => {
|
|
expr = &closure.body;
|
|
}
|
|
Gen(..)
|
|
| Block(..)
|
|
| ForLoop { .. }
|
|
| If(..)
|
|
| Loop(..)
|
|
| Match(..)
|
|
| Struct(..)
|
|
| TryBlock(..)
|
|
| While(..)
|
|
| ConstBlock(_) => break Some(TrailingBrace::Expr(expr)),
|
|
|
|
Cast(_, ty) => {
|
|
break type_trailing_braced_mac_call(ty).map(TrailingBrace::MacCall);
|
|
}
|
|
|
|
MacCall(mac) => {
|
|
break (mac.args.delim == Delimiter::Brace).then_some(TrailingBrace::MacCall(mac));
|
|
}
|
|
|
|
InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => {
|
|
// These should have been denied pre-expansion.
|
|
break None;
|
|
}
|
|
|
|
Break(_, None)
|
|
| Range(_, None, _)
|
|
| Ret(None)
|
|
| Yield(None)
|
|
| Array(_)
|
|
| Call(_, _)
|
|
| MethodCall(_)
|
|
| Tup(_)
|
|
| Lit(_)
|
|
| Type(_, _)
|
|
| Await(_, _)
|
|
| Field(_, _)
|
|
| Index(_, _, _)
|
|
| Underscore
|
|
| Path(_, _)
|
|
| Continue(_)
|
|
| Repeat(_, _)
|
|
| Paren(_)
|
|
| Try(_)
|
|
| Yeet(None)
|
|
| Err(_)
|
|
| Dummy => break None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// If the type's last token is `}`, it must be due to a braced macro call, such
|
|
/// as in `*const brace! { ... }`. Returns that trailing macro call.
|
|
fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
|
|
loop {
|
|
match &ty.kind {
|
|
ast::TyKind::MacCall(mac) => {
|
|
break (mac.args.delim == Delimiter::Brace).then_some(mac);
|
|
}
|
|
|
|
ast::TyKind::Ptr(mut_ty)
|
|
| ast::TyKind::Ref(_, mut_ty)
|
|
| ast::TyKind::PinnedRef(_, mut_ty) => {
|
|
ty = &mut_ty.ty;
|
|
}
|
|
|
|
ast::TyKind::BareFn(fn_ty) => match &fn_ty.decl.output {
|
|
ast::FnRetTy::Default(_) => break None,
|
|
ast::FnRetTy::Ty(ret) => ty = ret,
|
|
},
|
|
|
|
ast::TyKind::Path(_, path) => match path_return_type(path) {
|
|
Some(trailing_ty) => ty = trailing_ty,
|
|
None => break None,
|
|
},
|
|
|
|
ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
|
|
match bounds.last() {
|
|
Some(ast::GenericBound::Trait(bound)) => {
|
|
match path_return_type(&bound.trait_ref.path) {
|
|
Some(trailing_ty) => ty = trailing_ty,
|
|
None => break None,
|
|
}
|
|
}
|
|
Some(ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..)) | None => {
|
|
break None;
|
|
}
|
|
}
|
|
}
|
|
|
|
ast::TyKind::Slice(..)
|
|
| ast::TyKind::Array(..)
|
|
| ast::TyKind::Never
|
|
| ast::TyKind::Tup(..)
|
|
| ast::TyKind::Paren(..)
|
|
| ast::TyKind::Typeof(..)
|
|
| ast::TyKind::Infer
|
|
| ast::TyKind::ImplicitSelf
|
|
| ast::TyKind::CVarArgs
|
|
| ast::TyKind::Pat(..)
|
|
| ast::TyKind::Dummy
|
|
| ast::TyKind::Err(..) => break None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns the trailing return type in the given path, if it has one.
|
|
///
|
|
/// ```ignore (illustrative)
|
|
/// ::std::ops::FnOnce(&str) -> fn() -> *const c_void
|
|
/// ^^^^^^^^^^^^^^^^^^^^^
|
|
/// ```
|
|
fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> {
|
|
let last_segment = path.segments.last()?;
|
|
let args = last_segment.args.as_ref()?;
|
|
match &**args {
|
|
ast::GenericArgs::Parenthesized(args) => match &args.output {
|
|
ast::FnRetTy::Default(_) => None,
|
|
ast::FnRetTy::Ty(ret) => Some(ret),
|
|
},
|
|
ast::GenericArgs::AngleBracketed(_) | ast::GenericArgs::ParenthesizedElided(_) => None,
|
|
}
|
|
}
|