Skip to content

Commit

Permalink
Use single source of truth for expr precedence
Browse files Browse the repository at this point in the history
Introduce a new unified type that holds the expression precedence for
both AST and HIR nodes.
  • Loading branch information
estebank committed Jan 15, 2018
1 parent 09efaaf commit afe8d13
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 115 deletions.
64 changes: 63 additions & 1 deletion src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ use util::nodemap::{NodeMap, FxHashSet};
use syntax_pos::{Span, DUMMY_SP};
use syntax::codemap::{self, Spanned};
use syntax::abi::Abi;
use syntax::ast::{Ident, Name, NodeId, DUMMY_NODE_ID, AsmDialect};
use syntax::ast::{self, Ident, Name, NodeId, DUMMY_NODE_ID, AsmDialect};
use syntax::ast::{Attribute, Lit, StrStyle, FloatTy, IntTy, UintTy, MetaItem};
use syntax::ext::hygiene::SyntaxContext;
use syntax::ptr::P;
use syntax::symbol::{Symbol, keywords};
use syntax::tokenstream::TokenStream;
use syntax::util::ThinVec;
use syntax::ast::ExprPrecedence;
use ty::AdtKind;

use rustc_data_structures::indexed_vec;
Expand Down Expand Up @@ -958,6 +959,31 @@ impl BinOp_ {
}
}

impl Into<ast::BinOpKind> for BinOp_ {
fn into(self) -> ast::BinOpKind {
match self {
BiAdd => ast::BinOpKind::Add,
BiSub => ast::BinOpKind::Sub,
BiMul => ast::BinOpKind::Mul,
BiDiv => ast::BinOpKind::Div,
BiRem => ast::BinOpKind::Rem,
BiAnd => ast::BinOpKind::And,
BiOr => ast::BinOpKind::Or,
BiBitXor => ast::BinOpKind::BitXor,
BiBitAnd => ast::BinOpKind::BitAnd,
BiBitOr => ast::BinOpKind::BitOr,
BiShl => ast::BinOpKind::Shl,
BiShr => ast::BinOpKind::Shr,
BiEq => ast::BinOpKind::Eq,
BiLt => ast::BinOpKind::Lt,
BiLe => ast::BinOpKind::Le,
BiNe => ast::BinOpKind::Ne,
BiGe => ast::BinOpKind::Ge,
BiGt => ast::BinOpKind::Gt,
}
}
}

pub type BinOp = Spanned<BinOp_>;

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
Expand Down Expand Up @@ -1166,6 +1192,42 @@ pub struct Expr {
pub hir_id: HirId,
}

impl Expr {
pub fn precedence(&self) -> ExprPrecedence {
match self.node {
ExprBox(_) => ExprPrecedence::Box,
ExprArray(_) => ExprPrecedence::Array,
ExprCall(..) => ExprPrecedence::Call,
ExprMethodCall(..) => ExprPrecedence::MethodCall,
ExprTup(_) => ExprPrecedence::Tup,
ExprBinary(op, ..) => ExprPrecedence::Binary(op.node.into()),
ExprUnary(..) => ExprPrecedence::Unary,
ExprLit(_) => ExprPrecedence::Lit,
ExprType(..) | ExprCast(..) => ExprPrecedence::Cast,
ExprIf(..) => ExprPrecedence::If,
ExprWhile(..) => ExprPrecedence::While,
ExprLoop(..) => ExprPrecedence::Loop,
ExprMatch(..) => ExprPrecedence::Match,
ExprClosure(..) => ExprPrecedence::Closure,
ExprBlock(..) => ExprPrecedence::Block,
ExprAssign(..) => ExprPrecedence::Assign,
ExprAssignOp(..) => ExprPrecedence::AssignOp,
ExprField(..) => ExprPrecedence::Field,
ExprTupField(..) => ExprPrecedence::TupField,
ExprIndex(..) => ExprPrecedence::Index,
ExprPath(..) => ExprPrecedence::Path,
ExprAddrOf(..) => ExprPrecedence::AddrOf,
ExprBreak(..) => ExprPrecedence::Break,
ExprAgain(..) => ExprPrecedence::Continue,
ExprRet(..) => ExprPrecedence::Ret,
ExprInlineAsm(..) => ExprPrecedence::InlineAsm,
ExprStruct(..) => ExprPrecedence::Struct,
ExprRepeat(..) => ExprPrecedence::Repeat,
ExprYield(..) => ExprPrecedence::Yield,
}
}
}

impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "expr({}: {})", self.id,
Expand Down
51 changes: 1 addition & 50 deletions src/librustc/hir/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1104,7 +1104,7 @@ impl<'a> State<'a> {
}

pub fn print_expr_maybe_paren(&mut self, expr: &hir::Expr, prec: i8) -> io::Result<()> {
let needs_par = expr_precedence(expr) < prec;
let needs_par = expr.precedence().order() < prec;
if needs_par {
self.popen()?;
}
Expand Down Expand Up @@ -2318,55 +2318,6 @@ fn stmt_ends_with_semi(stmt: &hir::Stmt_) -> bool {
}
}


fn expr_precedence(expr: &hir::Expr) -> i8 {
use syntax::util::parser::*;

match expr.node {
hir::ExprClosure(..) => PREC_CLOSURE,

hir::ExprBreak(..) |
hir::ExprAgain(..) |
hir::ExprRet(..) |
hir::ExprYield(..) => PREC_JUMP,

// Binop-like expr kinds, handled by `AssocOp`.
hir::ExprBinary(op, _, _) => bin_op_to_assoc_op(op.node).precedence() as i8,

hir::ExprCast(..) => AssocOp::As.precedence() as i8,
hir::ExprType(..) => AssocOp::Colon.precedence() as i8,

hir::ExprAssign(..) |
hir::ExprAssignOp(..) => AssocOp::Assign.precedence() as i8,

// Unary, prefix
hir::ExprBox(..) |
hir::ExprAddrOf(..) |
hir::ExprUnary(..) => PREC_PREFIX,

// Unary, postfix
hir::ExprCall(..) |
hir::ExprMethodCall(..) |
hir::ExprField(..) |
hir::ExprTupField(..) |
hir::ExprIndex(..) |
hir::ExprInlineAsm(..) => PREC_POSTFIX,

// Never need parens
hir::ExprArray(..) |
hir::ExprRepeat(..) |
hir::ExprTup(..) |
hir::ExprLit(..) |
hir::ExprPath(..) |
hir::ExprIf(..) |
hir::ExprWhile(..) |
hir::ExprLoop(..) |
hir::ExprMatch(..) |
hir::ExprBlock(..) |
hir::ExprStruct(..) => PREC_PAREN,
}
}

fn bin_op_to_assoc_op(op: hir::BinOp_) -> AssocOp {
use hir::BinOp_::*;
match op {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rustc::infer::InferOk;
use rustc::traits::ObligationCause;

use syntax::ast;
use syntax::util::parser::{expr_precedence, AssocOp};
use syntax::util::parser::AssocOp;
use syntax_pos::{self, Span};
use rustc::hir;
use rustc::hir::print;
Expand Down Expand Up @@ -336,7 +336,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// For now, don't suggest casting with `as`.
let can_cast = false;

let needs_paren = expr_precedence(expr) < (AssocOp::As.precedence() as i8);
let needs_paren = expr.precedence().order() < (AssocOp::As.precedence() as i8);

if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
let msg = format!("you can cast an `{}` to `{}`", checked_ty, expected_ty);
Expand Down
180 changes: 180 additions & 0 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ pub use self::UnsafeSource::*;
pub use self::PathParameters::*;
pub use symbol::{Ident, Symbol as Name};
pub use util::ThinVec;
pub use util::parser::{
AssocOp,
PREC_RESET,
PREC_CLOSURE,
PREC_JUMP,
PREC_RANGE,
PREC_PREFIX,
PREC_POSTFIX,
PREC_PAREN,
PREC_FORCE_PAREN,
};

use syntax_pos::{Span, DUMMY_SP};
use codemap::{respan, Spanned};
Expand All @@ -28,6 +39,7 @@ use tokenstream::{ThinTokenStream, TokenStream};

use serialize::{self, Encoder, Decoder};
use std::collections::HashSet;
use std::cmp::Ordering;
use std::fmt;
use std::rc::Rc;
use std::u32;
Expand Down Expand Up @@ -730,6 +742,7 @@ impl BinOpKind {
_ => false
}
}

pub fn is_comparison(&self) -> bool {
use self::BinOpKind::*;
match *self {
Expand All @@ -740,6 +753,7 @@ impl BinOpKind {
false,
}
}

/// Returns `true` if the binary operator takes its arguments by value
pub fn is_by_value(&self) -> bool {
!self.is_comparison()
Expand Down Expand Up @@ -903,6 +917,129 @@ pub struct Expr {
pub attrs: ThinVec<Attribute>
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExprPrecedence {
Closure,
Break,
Continue,
Ret,
Yield,

Range,

Binary(BinOpKind),

InPlace,
Cast,
Type,

Assign,
AssignOp,

Box,
AddrOf,
Unary,

Call,
MethodCall,
Field,
TupField,
Index,
Try,
InlineAsm,
Mac,

Array,
Repeat,
Tup,
Lit,
Path,
Paren,
If,
IfLet,
While,
WhileLet,
ForLoop,
Loop,
Match,
Block,
Catch,
Struct,
}

impl PartialOrd for ExprPrecedence {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.order().cmp(&other.order()))
}
}

impl Ord for ExprPrecedence {
fn cmp(&self, other: &Self) -> Ordering {
self.order().cmp(&other.order())
}
}

impl ExprPrecedence {
pub fn order(self) -> i8 {
match self {
ExprPrecedence::Closure => PREC_CLOSURE,

ExprPrecedence::Break |
ExprPrecedence::Continue |
ExprPrecedence::Ret |
ExprPrecedence::Yield => PREC_JUMP,

// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
// parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
// ensures that `pprust` will add parentheses in the right places to get the desired
// parse.
ExprPrecedence::Range => PREC_RANGE,

// Binop-like expr kinds, handled by `AssocOp`.
ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8,
ExprPrecedence::InPlace => AssocOp::Inplace.precedence() as i8,
ExprPrecedence::Cast => AssocOp::As.precedence() as i8,
ExprPrecedence::Type => AssocOp::Colon.precedence() as i8,

ExprPrecedence::Assign |
ExprPrecedence::AssignOp => AssocOp::Assign.precedence() as i8,

// Unary, prefix
ExprPrecedence::Box |
ExprPrecedence::AddrOf |
ExprPrecedence::Unary => PREC_PREFIX,

// Unary, postfix
ExprPrecedence::Call |
ExprPrecedence::MethodCall |
ExprPrecedence::Field |
ExprPrecedence::TupField |
ExprPrecedence::Index |
ExprPrecedence::Try |
ExprPrecedence::InlineAsm |
ExprPrecedence::Mac => PREC_POSTFIX,

// Never need parens
ExprPrecedence::Array |
ExprPrecedence::Repeat |
ExprPrecedence::Tup |
ExprPrecedence::Lit |
ExprPrecedence::Path |
ExprPrecedence::Paren |
ExprPrecedence::If |
ExprPrecedence::IfLet |
ExprPrecedence::While |
ExprPrecedence::WhileLet |
ExprPrecedence::ForLoop |
ExprPrecedence::Loop |
ExprPrecedence::Match |
ExprPrecedence::Block |
ExprPrecedence::Catch |
ExprPrecedence::Struct => PREC_PAREN,
}
}
}

impl Expr {
/// Wether this expression would be valid somewhere that expects a value, for example, an `if`
/// condition.
Expand Down Expand Up @@ -966,6 +1103,49 @@ impl Expr {

Some(P(Ty { node, id: self.id, span: self.span }))
}

pub fn precedence(&self) -> ExprPrecedence {
match self.node {
ExprKind::Box(_) => ExprPrecedence::Box,
ExprKind::InPlace(..) => ExprPrecedence::InPlace,
ExprKind::Array(_) => ExprPrecedence::Array,
ExprKind::Call(..) => ExprPrecedence::Call,
ExprKind::MethodCall(..) => ExprPrecedence::MethodCall,
ExprKind::Tup(_) => ExprPrecedence::Tup,
ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node),
ExprKind::Unary(..) => ExprPrecedence::Unary,
ExprKind::Lit(_) => ExprPrecedence::Lit,
ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast,
ExprKind::If(..) => ExprPrecedence::If,
ExprKind::IfLet(..) => ExprPrecedence::IfLet,
ExprKind::While(..) => ExprPrecedence::While,
ExprKind::WhileLet(..) => ExprPrecedence::WhileLet,
ExprKind::ForLoop(..) => ExprPrecedence::ForLoop,
ExprKind::Loop(..) => ExprPrecedence::Loop,
ExprKind::Match(..) => ExprPrecedence::Match,
ExprKind::Closure(..) => ExprPrecedence::Closure,
ExprKind::Block(..) => ExprPrecedence::Block,
ExprKind::Catch(..) => ExprPrecedence::Catch,
ExprKind::Assign(..) => ExprPrecedence::Assign,
ExprKind::AssignOp(..) => ExprPrecedence::AssignOp,
ExprKind::Field(..) => ExprPrecedence::Field,
ExprKind::TupField(..) => ExprPrecedence::TupField,
ExprKind::Index(..) => ExprPrecedence::Index,
ExprKind::Range(..) => ExprPrecedence::Range,
ExprKind::Path(..) => ExprPrecedence::Path,
ExprKind::AddrOf(..) => ExprPrecedence::AddrOf,
ExprKind::Break(..) => ExprPrecedence::Break,
ExprKind::Continue(..) => ExprPrecedence::Continue,
ExprKind::Ret(..) => ExprPrecedence::Ret,
ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm,
ExprKind::Mac(..) => ExprPrecedence::Mac,
ExprKind::Struct(..) => ExprPrecedence::Struct,
ExprKind::Repeat(..) => ExprPrecedence::Repeat,
ExprKind::Paren(..) => ExprPrecedence::Paren,
ExprKind::Try(..) => ExprPrecedence::Try,
ExprKind::Yield(..) => ExprPrecedence::Yield,
}
}
}

impl fmt::Debug for Expr {
Expand Down
Loading

0 comments on commit afe8d13

Please sign in to comment.