2016-08-23 17:39:36 +00:00
//! Contains utility functions to generate suggestions.
#![ deny(missing_docs_in_private_items) ]
2017-08-21 11:32:12 +00:00
// currently ignores lifetimes and generics
#![ allow(use_self) ]
2016-08-23 17:39:36 +00:00
2016-06-29 19:23:21 +00:00
use rustc ::hir ;
2016-07-10 12:07:13 +00:00
use rustc ::lint ::{ EarlyContext , LateContext , LintContext } ;
use rustc_errors ;
2017-11-03 08:56:17 +00:00
use std ::borrow ::Cow ;
2016-07-10 12:07:13 +00:00
use std ::fmt ::Display ;
2016-06-29 19:23:21 +00:00
use std ;
2016-07-10 12:07:13 +00:00
use syntax ::codemap ::{ CharPos , Span } ;
2017-07-13 15:27:57 +00:00
use syntax ::parse ::token ;
use syntax ::print ::pprust ::token_to_string ;
2016-06-29 19:23:21 +00:00
use syntax ::util ::parser ::AssocOp ;
2016-07-10 12:07:13 +00:00
use syntax ::ast ;
2018-05-30 08:15:50 +00:00
use crate ::utils ::{ higher , snippet , snippet_opt } ;
2018-01-17 19:08:03 +00:00
use syntax_pos ::{ BytePos , Pos } ;
2016-06-29 19:23:21 +00:00
/// A helper type to build suggestion correctly handling parenthesis.
pub enum Sugg < ' a > {
/// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
NonParen ( Cow < ' a , str > ) ,
/// An expression that does not fit in other variants.
MaybeParen ( Cow < ' a , str > ) ,
2017-08-09 07:30:56 +00:00
/// A binary operator expression, including `as`-casts and explicit type
/// coercion.
2016-06-29 19:23:21 +00:00
BinOp ( AssocOp , Cow < ' a , str > ) ,
}
2016-07-01 17:30:38 +00:00
/// Literal constant `1`, for convenience.
pub const ONE : Sugg < 'static > = Sugg ::NonParen ( Cow ::Borrowed ( " 1 " ) ) ;
2016-07-10 12:07:13 +00:00
impl < ' a > Display for Sugg < ' a > {
2016-06-29 19:23:21 +00:00
fn fmt ( & self , f : & mut std ::fmt ::Formatter ) -> Result < ( ) , std ::fmt ::Error > {
match * self {
2017-09-05 09:33:04 +00:00
Sugg ::NonParen ( ref s ) | Sugg ::MaybeParen ( ref s ) | Sugg ::BinOp ( _ , ref s ) = > s . fmt ( f ) ,
2016-06-29 19:23:21 +00:00
}
}
}
2016-07-04 00:22:57 +00:00
#[ allow(wrong_self_convention) ] // ok, because of the function `as_ty` method
2016-06-29 19:23:21 +00:00
impl < ' a > Sugg < ' a > {
2016-08-23 17:39:36 +00:00
/// Prepare a suggestion from an expression.
2017-08-21 11:32:12 +00:00
pub fn hir_opt ( cx : & LateContext , expr : & hir ::Expr ) -> Option < Self > {
2016-07-02 15:24:24 +00:00
snippet_opt ( cx , expr . span ) . map ( | snippet | {
let snippet = Cow ::Owned ( snippet ) ;
match expr . node {
2018-07-12 07:30:57 +00:00
hir ::ExprKind ::AddrOf ( .. ) |
hir ::ExprKind ::Box ( .. ) |
hir ::ExprKind ::Closure ( .. , _ ) |
hir ::ExprKind ::If ( .. ) |
hir ::ExprKind ::Unary ( .. ) |
hir ::ExprKind ::Match ( .. ) = > Sugg ::MaybeParen ( snippet ) ,
hir ::ExprKind ::Continue ( .. ) |
hir ::ExprKind ::Yield ( .. ) |
hir ::ExprKind ::Array ( .. ) |
hir ::ExprKind ::Block ( .. ) |
hir ::ExprKind ::Break ( .. ) |
hir ::ExprKind ::Call ( .. ) |
hir ::ExprKind ::Field ( .. ) |
hir ::ExprKind ::Index ( .. ) |
hir ::ExprKind ::InlineAsm ( .. ) |
hir ::ExprKind ::Lit ( .. ) |
hir ::ExprKind ::Loop ( .. ) |
hir ::ExprKind ::MethodCall ( .. ) |
hir ::ExprKind ::Path ( .. ) |
hir ::ExprKind ::Repeat ( .. ) |
hir ::ExprKind ::Ret ( .. ) |
hir ::ExprKind ::Struct ( .. ) |
hir ::ExprKind ::Tup ( .. ) |
hir ::ExprKind ::While ( .. ) = > Sugg ::NonParen ( snippet ) ,
hir ::ExprKind ::Assign ( .. ) = > Sugg ::BinOp ( AssocOp ::Assign , snippet ) ,
hir ::ExprKind ::AssignOp ( op , .. ) = > Sugg ::BinOp ( hirbinop2assignop ( op ) , snippet ) ,
hir ::ExprKind ::Binary ( op , .. ) = > Sugg ::BinOp ( AssocOp ::from_ast_binop ( higher ::binop ( op . node ) ) , snippet ) ,
hir ::ExprKind ::Cast ( .. ) = > Sugg ::BinOp ( AssocOp ::As , snippet ) ,
hir ::ExprKind ::Type ( .. ) = > Sugg ::BinOp ( AssocOp ::Colon , snippet ) ,
2016-07-02 15:24:24 +00:00
}
} )
}
2016-06-29 19:23:21 +00:00
2017-08-09 07:30:56 +00:00
/// Convenience function around `hir_opt` for suggestions with a default
/// text.
2017-08-21 11:32:12 +00:00
pub fn hir ( cx : & LateContext , expr : & hir ::Expr , default : & ' a str ) -> Self {
2016-07-02 15:24:24 +00:00
Self ::hir_opt ( cx , expr ) . unwrap_or_else ( | | Sugg ::NonParen ( Cow ::Borrowed ( default ) ) )
2016-06-29 19:23:21 +00:00
}
2016-08-23 17:39:36 +00:00
/// Prepare a suggestion from an expression.
2017-08-21 11:32:12 +00:00
pub fn ast ( cx : & EarlyContext , expr : & ast ::Expr , default : & ' a str ) -> Self {
2016-06-29 19:23:21 +00:00
use syntax ::ast ::RangeLimits ;
let snippet = snippet ( cx , expr . span , default ) ;
match expr . node {
ast ::ExprKind ::AddrOf ( .. ) |
ast ::ExprKind ::Box ( .. ) |
ast ::ExprKind ::Closure ( .. ) |
ast ::ExprKind ::If ( .. ) |
ast ::ExprKind ::IfLet ( .. ) |
2018-05-29 08:56:58 +00:00
ast ::ExprKind ::ObsoleteInPlace ( .. ) |
2016-06-29 19:23:21 +00:00
ast ::ExprKind ::Unary ( .. ) |
ast ::ExprKind ::Match ( .. ) = > Sugg ::MaybeParen ( snippet ) ,
2018-06-24 21:42:52 +00:00
ast ::ExprKind ::Async ( .. ) |
2016-06-29 19:23:21 +00:00
ast ::ExprKind ::Block ( .. ) |
ast ::ExprKind ::Break ( .. ) |
ast ::ExprKind ::Call ( .. ) |
2017-03-15 23:56:21 +00:00
ast ::ExprKind ::Catch ( .. ) |
2016-07-01 15:49:05 +00:00
ast ::ExprKind ::Continue ( .. ) |
2017-08-30 08:54:24 +00:00
ast ::ExprKind ::Yield ( .. ) |
2016-06-29 19:23:21 +00:00
ast ::ExprKind ::Field ( .. ) |
ast ::ExprKind ::ForLoop ( .. ) |
ast ::ExprKind ::Index ( .. ) |
ast ::ExprKind ::InlineAsm ( .. ) |
ast ::ExprKind ::Lit ( .. ) |
ast ::ExprKind ::Loop ( .. ) |
ast ::ExprKind ::Mac ( .. ) |
ast ::ExprKind ::MethodCall ( .. ) |
ast ::ExprKind ::Paren ( .. ) |
ast ::ExprKind ::Path ( .. ) |
ast ::ExprKind ::Repeat ( .. ) |
ast ::ExprKind ::Ret ( .. ) |
ast ::ExprKind ::Struct ( .. ) |
ast ::ExprKind ::Try ( .. ) |
ast ::ExprKind ::Tup ( .. ) |
2017-01-18 20:35:38 +00:00
ast ::ExprKind ::Array ( .. ) |
2016-06-29 19:23:21 +00:00
ast ::ExprKind ::While ( .. ) |
ast ::ExprKind ::WhileLet ( .. ) = > Sugg ::NonParen ( snippet ) ,
ast ::ExprKind ::Range ( .. , RangeLimits ::HalfOpen ) = > Sugg ::BinOp ( AssocOp ::DotDot , snippet ) ,
2017-09-28 14:11:34 +00:00
ast ::ExprKind ::Range ( .. , RangeLimits ::Closed ) = > Sugg ::BinOp ( AssocOp ::DotDotEq , snippet ) ,
2016-06-29 19:23:21 +00:00
ast ::ExprKind ::Assign ( .. ) = > Sugg ::BinOp ( AssocOp ::Assign , snippet ) ,
ast ::ExprKind ::AssignOp ( op , .. ) = > Sugg ::BinOp ( astbinop2assignop ( op ) , snippet ) ,
ast ::ExprKind ::Binary ( op , .. ) = > Sugg ::BinOp ( AssocOp ::from_ast_binop ( op . node ) , snippet ) ,
ast ::ExprKind ::Cast ( .. ) = > Sugg ::BinOp ( AssocOp ::As , snippet ) ,
ast ::ExprKind ::Type ( .. ) = > Sugg ::BinOp ( AssocOp ::Colon , snippet ) ,
}
}
2016-07-01 17:30:38 +00:00
/// Convenience method to create the `<lhs> && <rhs>` suggestion.
2017-11-03 08:56:17 +00:00
pub fn and ( self , rhs : & Self ) -> Sugg < 'static > {
make_binop ( ast ::BinOpKind ::And , & self , rhs )
2016-06-29 19:23:21 +00:00
}
2016-06-29 20:37:10 +00:00
2016-07-04 00:22:57 +00:00
/// Convenience method to create the `<lhs> as <rhs>` suggestion.
2016-07-10 12:07:13 +00:00
pub fn as_ty < R : Display > ( self , rhs : R ) -> Sugg < 'static > {
2016-07-06 13:36:42 +00:00
make_assoc ( AssocOp ::As , & self , & Sugg ::NonParen ( rhs . to_string ( ) . into ( ) ) )
2016-07-04 00:22:57 +00:00
}
2016-06-29 20:37:10 +00:00
/// Convenience method to create the `&<expr>` suggestion.
2016-07-01 17:30:38 +00:00
pub fn addr ( self ) -> Sugg < 'static > {
2016-07-01 18:55:45 +00:00
make_unop ( " & " , self )
2016-07-01 17:30:38 +00:00
}
2016-07-02 15:24:24 +00:00
/// Convenience method to create the `&mut <expr>` suggestion.
pub fn mut_addr ( self ) -> Sugg < 'static > {
make_unop ( " &mut " , self )
}
2016-07-03 17:13:01 +00:00
/// Convenience method to create the `*<expr>` suggestion.
pub fn deref ( self ) -> Sugg < 'static > {
make_unop ( " * " , self )
}
2018-04-11 09:17:59 +00:00
/// Convenience method to create the `&*<expr>` suggestion. Currently this
/// is needed because `sugg.deref().addr()` produces an unnecessary set of
/// parentheses around the deref.
pub fn addr_deref ( self ) -> Sugg < 'static > {
make_unop ( " &* " , self )
}
/// Convenience method to create the `&mut *<expr>` suggestion. Currently
/// this is needed because `sugg.deref().mut_addr()` produces an unnecessary
/// set of parentheses around the deref.
pub fn mut_addr_deref ( self ) -> Sugg < 'static > {
make_unop ( " &mut * " , self )
}
2017-08-09 07:30:56 +00:00
/// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
/// suggestion.
2017-11-03 08:56:17 +00:00
pub fn range ( self , end : & Self , limit : ast ::RangeLimits ) -> Sugg < 'static > {
2016-07-01 17:30:38 +00:00
match limit {
2017-11-03 08:56:17 +00:00
ast ::RangeLimits ::HalfOpen = > make_assoc ( AssocOp ::DotDot , & self , end ) ,
ast ::RangeLimits ::Closed = > make_assoc ( AssocOp ::DotDotEq , & self , end ) ,
2016-07-01 17:30:38 +00:00
}
}
2016-07-01 18:55:45 +00:00
2017-08-09 07:30:56 +00:00
/// Add parenthesis to any expression that might need them. Suitable to the
/// `self` argument of
2016-07-01 18:55:45 +00:00
/// a method call (eg. to build `bar.foo()` or `(1 + 2).foo()`).
pub fn maybe_par ( self ) -> Self {
match self {
Sugg ::NonParen ( .. ) = > self ,
2016-08-08 14:43:45 +00:00
// (x) and (x).y() both don't need additional parens
2017-09-05 09:33:04 +00:00
Sugg ::MaybeParen ( sugg ) = > if sugg . starts_with ( '(' ) & & sugg . ends_with ( ')' ) {
Sugg ::MaybeParen ( sugg )
} else {
Sugg ::NonParen ( format! ( " ( {} ) " , sugg ) . into ( ) )
2016-08-08 14:43:45 +00:00
} ,
Sugg ::BinOp ( _ , sugg ) = > Sugg ::NonParen ( format! ( " ( {} ) " , sugg ) . into ( ) ) ,
2016-07-01 18:55:45 +00:00
}
}
2016-07-01 17:30:38 +00:00
}
impl < ' a , ' b > std ::ops ::Add < Sugg < ' b > > for Sugg < ' a > {
type Output = Sugg < 'static > ;
fn add ( self , rhs : Sugg < ' b > ) -> Sugg < 'static > {
make_binop ( ast ::BinOpKind ::Add , & self , & rhs )
2016-06-29 20:37:10 +00:00
}
2016-06-29 19:23:21 +00:00
}
2016-07-01 17:30:38 +00:00
impl < ' a , ' b > std ::ops ::Sub < Sugg < ' b > > for Sugg < ' a > {
2016-06-29 19:23:21 +00:00
type Output = Sugg < 'static > ;
2016-07-01 17:30:38 +00:00
fn sub ( self , rhs : Sugg < ' b > ) -> Sugg < 'static > {
make_binop ( ast ::BinOpKind ::Sub , & self , & rhs )
2016-06-29 19:23:21 +00:00
}
}
2016-07-01 17:30:38 +00:00
impl < ' a > std ::ops ::Not for Sugg < ' a > {
2016-06-29 19:50:21 +00:00
type Output = Sugg < 'static > ;
fn not ( self ) -> Sugg < 'static > {
2016-07-01 18:55:45 +00:00
make_unop ( " ! " , self )
2016-06-29 19:50:21 +00:00
}
}
2016-08-23 17:39:36 +00:00
/// Helper type to display either `foo` or `(foo)`.
2016-06-29 19:23:21 +00:00
struct ParenHelper < T > {
2016-08-23 17:51:12 +00:00
/// Whether parenthesis are needed.
2016-06-29 19:23:21 +00:00
paren : bool ,
2016-08-23 17:39:36 +00:00
/// The main thing to display.
2016-06-29 19:23:21 +00:00
wrapped : T ,
}
impl < T > ParenHelper < T > {
2016-08-23 17:39:36 +00:00
/// Build a `ParenHelper`.
2016-06-29 19:23:21 +00:00
fn new ( paren : bool , wrapped : T ) -> Self {
2017-08-21 11:32:12 +00:00
Self {
2018-03-15 15:07:15 +00:00
paren ,
wrapped ,
2016-06-29 19:23:21 +00:00
}
}
}
2016-07-10 12:07:13 +00:00
impl < T : Display > Display for ParenHelper < T > {
2016-06-29 19:23:21 +00:00
fn fmt ( & self , f : & mut std ::fmt ::Formatter ) -> Result < ( ) , std ::fmt ::Error > {
if self . paren {
write! ( f , " ({}) " , self . wrapped )
} else {
self . wrapped . fmt ( f )
}
}
}
2016-07-01 18:55:45 +00:00
/// Build the string for `<op><expr>` adding parenthesis when necessary.
2016-06-29 19:50:21 +00:00
///
2017-08-09 07:30:56 +00:00
/// For convenience, the operator is taken as a string because all unary
/// operators have the same
2016-06-29 19:50:21 +00:00
/// precedence.
2016-07-01 18:55:45 +00:00
pub fn make_unop ( op : & str , expr : Sugg ) -> Sugg < 'static > {
Sugg ::MaybeParen ( format! ( " {} {} " , op , expr . maybe_par ( ) ) . into ( ) )
2016-06-29 19:50:21 +00:00
}
2016-06-29 19:23:21 +00:00
/// Build the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
///
2017-08-09 07:30:56 +00:00
/// Precedence of shift operator relative to other arithmetic operation is
/// often confusing so
2016-06-29 19:23:21 +00:00
/// parenthesis will always be added for a mix of these.
2016-07-01 17:30:38 +00:00
pub fn make_assoc ( op : AssocOp , lhs : & Sugg , rhs : & Sugg ) -> Sugg < 'static > {
2016-08-23 17:51:12 +00:00
/// Whether the operator is a shift operator `<<` or `>>`.
2016-06-29 19:23:21 +00:00
fn is_shift ( op : & AssocOp ) -> bool {
matches! ( * op , AssocOp ::ShiftLeft | AssocOp ::ShiftRight )
}
2016-08-23 17:51:12 +00:00
/// Whether the operator is a arithmetic operator (`+`, `-`, `*`, `/`, `%`).
2016-06-29 19:23:21 +00:00
fn is_arith ( op : & AssocOp ) -> bool {
2017-08-09 07:30:56 +00:00
matches! (
* op ,
AssocOp ::Add | AssocOp ::Subtract | AssocOp ::Multiply | AssocOp ::Divide | AssocOp ::Modulus
)
2016-06-29 19:23:21 +00:00
}
2017-08-09 07:30:56 +00:00
/// Whether the operator `op` needs parenthesis with the operator `other`
/// in the direction
2016-08-23 17:39:36 +00:00
/// `dir`.
2016-06-29 19:23:21 +00:00
fn needs_paren ( op : & AssocOp , other : & AssocOp , dir : Associativity ) -> bool {
2017-11-04 19:55:56 +00:00
other . precedence ( ) < op . precedence ( )
| | ( other . precedence ( ) = = op . precedence ( )
& & ( ( op ! = other & & associativity ( op ) ! = dir )
| | ( op = = other & & associativity ( op ) ! = Associativity ::Both ) ) )
| | is_shift ( op ) & & is_arith ( other ) | | is_shift ( other ) & & is_arith ( op )
2016-06-29 19:23:21 +00:00
}
let lhs_paren = if let Sugg ::BinOp ( ref lop , _ ) = * lhs {
2016-07-01 17:30:38 +00:00
needs_paren ( & op , lop , Associativity ::Left )
2016-06-29 19:23:21 +00:00
} else {
false
} ;
let rhs_paren = if let Sugg ::BinOp ( ref rop , _ ) = * rhs {
2016-07-01 17:30:38 +00:00
needs_paren ( & op , rop , Associativity ::Right )
2016-06-29 19:23:21 +00:00
} else {
false
} ;
2016-07-01 17:30:38 +00:00
let lhs = ParenHelper ::new ( lhs_paren , lhs ) ;
let rhs = ParenHelper ::new ( rhs_paren , rhs ) ;
let sugg = match op {
2017-09-05 09:33:04 +00:00
AssocOp ::Add |
AssocOp ::BitAnd |
AssocOp ::BitOr |
AssocOp ::BitXor |
AssocOp ::Divide |
AssocOp ::Equal |
AssocOp ::Greater |
AssocOp ::GreaterEqual |
AssocOp ::LAnd |
AssocOp ::LOr |
AssocOp ::Less |
AssocOp ::LessEqual |
AssocOp ::Modulus |
AssocOp ::Multiply |
AssocOp ::NotEqual |
AssocOp ::ShiftLeft |
AssocOp ::ShiftRight |
AssocOp ::Subtract = > format! ( " {} {} {} " , lhs , op . to_ast_binop ( ) . expect ( " Those are AST ops " ) . to_string ( ) , rhs ) ,
2016-07-01 17:30:38 +00:00
AssocOp ::Assign = > format! ( " {} = {} " , lhs , rhs ) ,
2018-05-29 08:56:58 +00:00
AssocOp ::ObsoleteInPlace = > format! ( " in ( {} ) {} " , lhs , rhs ) ,
2017-07-13 15:27:57 +00:00
AssocOp ::AssignOp ( op ) = > format! ( " {} {} = {} " , lhs , token_to_string ( & token ::BinOp ( op ) ) , rhs ) ,
2016-07-01 17:30:38 +00:00
AssocOp ::As = > format! ( " {} as {} " , lhs , rhs ) ,
AssocOp ::DotDot = > format! ( " {} .. {} " , lhs , rhs ) ,
2017-09-28 17:35:14 +00:00
AssocOp ::DotDotEq = > format! ( " {} ..= {} " , lhs , rhs ) ,
2016-07-01 17:30:38 +00:00
AssocOp ::Colon = > format! ( " {} : {} " , lhs , rhs ) ,
} ;
Sugg ::BinOp ( op , sugg . into ( ) )
}
/// Convinience wrapper arround `make_assoc` and `AssocOp::from_ast_binop`.
pub fn make_binop ( op : ast ::BinOpKind , lhs : & Sugg , rhs : & Sugg ) -> Sugg < 'static > {
make_assoc ( AssocOp ::from_ast_binop ( op ) , lhs , rhs )
2016-06-29 19:23:21 +00:00
}
2017-02-20 03:50:31 +00:00
#[ derive(PartialEq, Eq, Clone, Copy) ]
2016-08-23 17:39:36 +00:00
/// Operator associativity.
2016-06-29 19:23:21 +00:00
enum Associativity {
2016-08-23 17:39:36 +00:00
/// The operator is both left-associative and right-associative.
2016-06-29 19:23:21 +00:00
Both ,
2016-08-23 17:39:36 +00:00
/// The operator is left-associative.
2016-06-29 19:23:21 +00:00
Left ,
2016-08-23 17:39:36 +00:00
/// The operator is not associative.
2016-06-29 19:23:21 +00:00
None ,
2016-08-23 17:39:36 +00:00
/// The operator is right-associative.
2016-06-29 19:23:21 +00:00
Right ,
}
2017-08-09 07:30:56 +00:00
/// Return the associativity/fixity of an operator. The difference with
/// `AssocOp::fixity` is that
2016-06-29 19:23:21 +00:00
/// an operator can be both left and right associative (such as `+`:
/// `a + b + c == (a + b) + c == a + (b + c)`.
///
2017-08-09 07:30:56 +00:00
/// Chained `as` and explicit `:` type coercion never need inner parenthesis so
/// they are considered
2016-06-29 19:23:21 +00:00
/// associative.
fn associativity ( op : & AssocOp ) -> Associativity {
use syntax ::util ::parser ::AssocOp ::* ;
match * op {
2018-05-29 08:56:58 +00:00
ObsoleteInPlace | Assign | AssignOp ( _ ) = > Associativity ::Right ,
2016-12-20 17:21:30 +00:00
Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon = > Associativity ::Both ,
2017-09-05 09:33:04 +00:00
Divide |
Equal |
Greater |
GreaterEqual |
Less |
LessEqual |
Modulus |
NotEqual |
ShiftLeft |
ShiftRight |
2016-12-20 17:21:30 +00:00
Subtract = > Associativity ::Left ,
2017-09-28 14:11:34 +00:00
DotDot | DotDotEq = > Associativity ::None ,
2016-06-29 19:23:21 +00:00
}
}
/// Convert a `hir::BinOp` to the corresponding assigning binary operator.
fn hirbinop2assignop ( op : hir ::BinOp ) -> AssocOp {
2018-07-12 07:50:09 +00:00
use rustc ::hir ::BinOpKind ::* ;
2016-06-29 19:23:21 +00:00
use syntax ::parse ::token ::BinOpToken ::* ;
AssocOp ::AssignOp ( match op . node {
2018-07-12 07:50:09 +00:00
BinOpKind ::Add = > Plus ,
BinOpKind ::BitAnd = > And ,
BinOpKind ::BitOr = > Or ,
BinOpKind ::BitXor = > Caret ,
BinOpKind ::Div = > Slash ,
BinOpKind ::Mul = > Star ,
BinOpKind ::Rem = > Percent ,
BinOpKind ::Shl = > Shl ,
BinOpKind ::Shr = > Shr ,
BinOpKind ::Sub = > Minus ,
BinOpKind ::And | BinOpKind ::Eq | BinOpKind ::Ge | BinOpKind ::Gt | BinOpKind ::Le | BinOpKind ::Lt | BinOpKind ::Ne | BinOpKind ::Or = > panic! ( " This operator does not exist " ) ,
2016-06-29 19:23:21 +00:00
} )
}
/// Convert an `ast::BinOp` to the corresponding assigning binary operator.
fn astbinop2assignop ( op : ast ::BinOp ) -> AssocOp {
use syntax ::ast ::BinOpKind ::* ;
use syntax ::parse ::token ::BinOpToken ;
AssocOp ::AssignOp ( match op . node {
Add = > BinOpToken ::Plus ,
BitAnd = > BinOpToken ::And ,
BitOr = > BinOpToken ::Or ,
BitXor = > BinOpToken ::Caret ,
Div = > BinOpToken ::Slash ,
Mul = > BinOpToken ::Star ,
Rem = > BinOpToken ::Percent ,
Shl = > BinOpToken ::Shl ,
Shr = > BinOpToken ::Shr ,
Sub = > BinOpToken ::Minus ,
And | Eq | Ge | Gt | Le | Lt | Ne | Or = > panic! ( " This operator does not exist " ) ,
} )
}
2016-07-10 12:07:13 +00:00
2017-08-09 07:30:56 +00:00
/// Return the indentation before `span` if there are nothing but `[ \t]`
/// before it on its line.
2016-12-06 10:32:21 +00:00
fn indentation < ' a , T : LintContext < ' a > > ( cx : & T , span : Span ) -> Option < String > {
2017-08-31 12:47:45 +00:00
let lo = cx . sess ( ) . codemap ( ) . lookup_char_pos ( span . lo ( ) ) ;
2017-09-05 09:33:04 +00:00
if let Some ( line ) = lo . file
. get_line ( lo . line - 1 /* line numbers in `Loc` are 1-based */ )
2017-08-09 07:30:56 +00:00
{
2016-07-10 12:07:13 +00:00
if let Some ( ( pos , _ ) ) = line . char_indices ( ) . find ( | & ( _ , c ) | c ! = ' ' & & c ! = '\t' ) {
// we can mix char and byte positions here because we only consider `[ \t]`
if lo . col = = CharPos ( pos ) {
Some ( line [ .. pos ] . into ( ) )
} else {
None
}
} else {
None
}
} else {
None
}
}
2016-08-23 17:39:36 +00:00
/// Convenience extension trait for `DiagnosticBuilder`.
2016-12-06 10:32:21 +00:00
pub trait DiagnosticBuilderExt < ' a , T : LintContext < ' a > > {
2016-07-10 12:07:13 +00:00
/// Suggests to add an attribute to an item.
///
/// Correctly handles indentation of the attribute and item.
///
/// # Example
///
2016-12-21 09:00:13 +00:00
/// ```rust,ignore
2016-07-10 12:07:13 +00:00
/// db.suggest_item_with_attr(cx, item, "#[derive(Default)]");
/// ```
2016-12-20 17:21:30 +00:00
fn suggest_item_with_attr < D : Display + ? Sized > ( & mut self , cx : & T , item : Span , msg : & str , attr : & D ) ;
2016-07-10 12:07:13 +00:00
/// Suggest to add an item before another.
///
/// The item should not be indented (expect for inner indentation).
///
/// # Example
///
2016-12-21 09:00:13 +00:00
/// ```rust,ignore
2016-07-10 12:07:13 +00:00
/// db.suggest_prepend_item(cx, item,
/// "fn foo() {
/// bar();
/// }");
/// ```
fn suggest_prepend_item ( & mut self , cx : & T , item : Span , msg : & str , new_item : & str ) ;
2018-01-17 19:08:03 +00:00
/// Suggest to completely remove an item.
///
/// This will remove an item and all following whitespace until the next non-whitespace
/// character. This should work correctly if item is on the same indentation level as the
/// following item.
///
/// # Example
///
/// ```rust,ignore
/// db.suggest_remove_item(cx, item, "remove this")
/// ```
fn suggest_remove_item ( & mut self , cx : & T , item : Span , msg : & str ) ;
2016-07-10 12:07:13 +00:00
}
2016-12-06 10:32:21 +00:00
impl < ' a , ' b , ' c , T : LintContext < ' c > > DiagnosticBuilderExt < ' c , T > for rustc_errors ::DiagnosticBuilder < ' b > {
2016-12-20 17:21:30 +00:00
fn suggest_item_with_attr < D : Display + ? Sized > ( & mut self , cx : & T , item : Span , msg : & str , attr : & D ) {
2016-07-10 12:07:13 +00:00
if let Some ( indent ) = indentation ( cx , item ) {
2017-08-31 12:47:45 +00:00
let span = item . with_hi ( item . lo ( ) ) ;
2016-07-10 12:07:13 +00:00
self . span_suggestion ( span , msg , format! ( " {} \n {} " , attr , indent ) ) ;
}
}
fn suggest_prepend_item ( & mut self , cx : & T , item : Span , msg : & str , new_item : & str ) {
if let Some ( indent ) = indentation ( cx , item ) {
2017-08-31 12:47:45 +00:00
let span = item . with_hi ( item . lo ( ) ) ;
2016-07-10 12:07:13 +00:00
let mut first = true ;
2017-08-09 07:30:56 +00:00
let new_item = new_item
. lines ( )
2017-11-04 19:55:56 +00:00
. map ( | l | {
if first {
first = false ;
format! ( " {} \n " , l )
} else {
format! ( " {} {} \n " , indent , l )
}
2016-12-20 17:21:30 +00:00
} )
. collect ::< String > ( ) ;
2016-07-10 12:07:13 +00:00
self . span_suggestion ( span , msg , format! ( " {} \n {} " , new_item , indent ) ) ;
}
}
2018-01-17 19:08:03 +00:00
fn suggest_remove_item ( & mut self , cx : & T , item : Span , msg : & str ) {
let mut remove_span = item ;
2018-01-28 07:28:48 +00:00
let hi = cx . sess ( ) . codemap ( ) . next_point ( remove_span ) . hi ( ) ;
let fmpos = cx . sess ( ) . codemap ( ) . lookup_byte_offset ( hi ) ;
2018-01-17 19:08:03 +00:00
if let Some ( ref src ) = fmpos . fm . src {
let non_whitespace_offset = src [ fmpos . pos . to_usize ( ) .. ] . find ( | c | c ! = ' ' & & c ! = '\t' & & c ! = '\n' ) ;
if let Some ( non_whitespace_offset ) = non_whitespace_offset {
remove_span = remove_span . with_hi ( remove_span . hi ( ) + BytePos ( non_whitespace_offset as u32 ) )
}
}
self . span_suggestion ( remove_span , msg , String ::new ( ) ) ;
}
2016-07-10 12:07:13 +00:00
}