2021-03-25 13:29:11 -05:00
use clippy_utils ::diagnostics ::{ span_lint , span_lint_and_note } ;
use clippy_utils ::{ get_parent_expr , path_to_local , path_to_local_id } ;
2021-05-06 04:51:22 -05:00
use if_chain ::if_chain ;
2022-01-15 16:07:52 -06:00
use rustc_hir ::intravisit ::{ walk_expr , Visitor } ;
2021-02-11 08:04:38 -06:00
use rustc_hir ::{ BinOpKind , Block , Expr , ExprKind , Guard , HirId , Local , Node , Stmt , StmtKind } ;
2020-01-12 00:08:41 -06:00
use rustc_lint ::{ LateContext , LateLintPass } ;
2020-03-30 04:02:14 -05:00
use rustc_middle ::ty ;
2020-01-11 05:37:08 -06:00
use rustc_session ::{ declare_lint_pass , declare_tool_lint } ;
2016-08-10 22:16:28 -05:00
2018-03-28 08:24:26 -05:00
declare_clippy_lint! {
2021-07-29 05:16:06 -05:00
/// ### What it does
/// Checks for a read and a write to the same variable where
2019-03-05 10:50:33 -06:00
/// whether the read occurs before or after the write depends on the evaluation
/// order of sub-expressions.
///
2021-07-29 05:16:06 -05:00
/// ### Why is this bad?
2021-09-08 09:31:47 -05:00
/// It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands),
/// the operands of these expressions are evaluated before applying the effects of the expression.
2019-03-05 10:50:33 -06:00
///
2021-07-29 05:16:06 -05:00
/// ### Known problems
/// Code which intentionally depends on the evaluation
2019-03-05 10:50:33 -06:00
/// order, or which is correct for any evaluation order.
///
2021-07-29 05:16:06 -05:00
/// ### Example
2019-03-05 10:50:33 -06:00
/// ```rust
/// let mut x = 0;
2020-06-09 09:36:01 -05:00
///
2019-03-05 10:50:33 -06:00
/// let a = {
/// x = 1;
/// 1
/// } + x;
/// // Unclear whether a is 1 or 2.
2022-06-05 14:24:41 -05:00
/// ```
2020-06-09 09:36:01 -05:00
///
2022-06-05 14:24:41 -05:00
/// Use instead:
/// ```rust
/// # let mut x = 0;
2020-06-09 09:36:01 -05:00
/// let tmp = {
/// x = 1;
/// 1
/// };
/// let a = tmp + x;
2019-03-05 10:50:33 -06:00
/// ```
2021-12-06 05:33:31 -06:00
#[ clippy::version = " pre 1.29.0 " ]
2022-05-21 06:24:00 -05:00
pub MIXED_READ_WRITE_IN_EXPRESSION ,
restriction ,
2016-08-10 22:16:28 -05:00
" whether a variable read occurs before a write depends on sub-expression evaluation order "
}
2018-03-28 08:24:26 -05:00
declare_clippy_lint! {
2021-07-29 05:16:06 -05:00
/// ### What it does
/// Checks for diverging calls that are not match arms or
2019-03-05 10:50:33 -06:00
/// statements.
///
2021-07-29 05:16:06 -05:00
/// ### Why is this bad?
/// It is often confusing to read. In addition, the
2019-03-05 10:50:33 -06:00
/// sub-expression evaluation order for Rust is not well documented.
///
2021-07-29 05:16:06 -05:00
/// ### Known problems
/// Someone might want to use `some_bool || panic!()` as a
2019-03-05 10:50:33 -06:00
/// shorthand.
///
2021-07-29 05:16:06 -05:00
/// ### Example
2019-08-02 01:13:54 -05:00
/// ```rust,no_run
/// # fn b() -> bool { true }
/// # fn c() -> bool { true }
2019-03-05 10:50:33 -06:00
/// let a = b() || panic!() || c();
/// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
/// let x = (a, b, c, panic!());
/// // can simply be replaced by `panic!()`
/// ```
2021-12-06 05:33:31 -06:00
#[ clippy::version = " pre 1.29.0 " ]
2016-09-06 08:15:36 -05:00
pub DIVERGING_SUB_EXPRESSION ,
2018-03-28 08:24:26 -05:00
complexity ,
2016-09-06 08:15:36 -05:00
" whether an expression contains a diverging sub expression "
}
2022-05-21 06:24:00 -05:00
declare_lint_pass! ( EvalOrderDependence = > [ MIXED_READ_WRITE_IN_EXPRESSION , DIVERGING_SUB_EXPRESSION ] ) ;
2016-08-10 22:16:28 -05:00
2020-06-25 15:41:36 -05:00
impl < ' tcx > LateLintPass < ' tcx > for EvalOrderDependence {
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < '_ > ) {
2016-08-10 22:16:28 -05:00
// Find a write to a local variable.
2021-05-06 04:51:22 -05:00
let var = if_chain! {
if let ExprKind ::Assign ( lhs , .. ) | ExprKind ::AssignOp ( _ , lhs , _ ) = expr . kind ;
if let Some ( var ) = path_to_local ( lhs ) ;
if expr . span . desugaring_kind ( ) . is_none ( ) ;
then { var } else { return ; }
} ;
let mut visitor = ReadVisitor {
cx ,
var ,
write_expr : expr ,
last_expr : expr ,
} ;
check_for_unsequenced_reads ( & mut visitor ) ;
2016-08-10 22:16:28 -05:00
}
2020-06-25 15:41:36 -05:00
fn check_stmt ( & mut self , cx : & LateContext < ' tcx > , stmt : & ' tcx Stmt < '_ > ) {
2019-09-27 10:16:06 -05:00
match stmt . kind {
2021-04-08 10:50:13 -05:00
StmtKind ::Local ( local ) = > {
if let Local { init : Some ( e ) , .. } = local {
2019-01-20 04:49:45 -06:00
DivergenceVisitor { cx } . visit_expr ( e ) ;
}
2016-09-06 08:15:36 -05:00
} ,
2021-04-08 10:50:13 -05:00
StmtKind ::Expr ( e ) | StmtKind ::Semi ( e ) = > DivergenceVisitor { cx } . maybe_walk_expr ( e ) ,
2019-01-20 04:21:30 -06:00
StmtKind ::Item ( .. ) = > { } ,
2016-09-06 08:15:36 -05:00
}
}
}
2019-06-19 13:36:23 -05:00
struct DivergenceVisitor < ' a , ' tcx > {
2020-06-25 15:41:36 -05:00
cx : & ' a LateContext < ' tcx > ,
2016-12-06 04:32:21 -06:00
}
2016-09-06 08:15:36 -05:00
impl < ' a , ' tcx > DivergenceVisitor < ' a , ' tcx > {
2019-12-27 01:12:26 -06:00
fn maybe_walk_expr ( & mut self , e : & ' tcx Expr < '_ > ) {
2019-09-27 10:16:06 -05:00
match e . kind {
2022-06-11 14:25:25 -05:00
ExprKind ::Closure { .. } = > { } ,
2021-04-08 10:50:13 -05:00
ExprKind ::Match ( e , arms , _ ) = > {
2016-09-06 08:15:36 -05:00
self . visit_expr ( e ) ;
for arm in arms {
2020-03-16 01:23:03 -05:00
if let Some ( Guard ::If ( if_expr ) ) = arm . guard {
2021-06-03 01:41:37 -05:00
self . visit_expr ( if_expr ) ;
2016-09-06 08:15:36 -05:00
}
// make sure top level arm expressions aren't linted
2022-06-04 06:34:07 -05:00
self . maybe_walk_expr ( arm . body ) ;
2016-09-06 08:15:36 -05:00
}
2016-12-20 11:21:30 -06:00
} ,
2016-09-06 08:15:36 -05:00
_ = > walk_expr ( self , e ) ,
}
}
2019-12-27 01:12:26 -06:00
fn report_diverging_sub_expr ( & mut self , e : & Expr < '_ > ) {
2016-12-20 11:21:30 -06:00
span_lint ( self . cx , DIVERGING_SUB_EXPRESSION , e . span , " sub-expression diverges " ) ;
2016-09-06 08:15:36 -05:00
}
}
2016-12-06 04:32:21 -06:00
impl < ' a , ' tcx > Visitor < ' tcx > for DivergenceVisitor < ' a , ' tcx > {
2019-12-27 01:12:26 -06:00
fn visit_expr ( & mut self , e : & ' tcx Expr < '_ > ) {
2019-09-27 10:16:06 -05:00
match e . kind {
2018-07-12 02:30:57 -05:00
ExprKind ::Continue ( _ ) | ExprKind ::Break ( _ , _ ) | ExprKind ::Ret ( _ ) = > self . report_diverging_sub_expr ( e ) ,
2021-04-08 10:50:13 -05:00
ExprKind ::Call ( func , _ ) = > {
2020-07-17 03:47:04 -05:00
let typ = self . cx . typeck_results ( ) . expr_ty ( func ) ;
2020-08-03 17:18:29 -05:00
match typ . kind ( ) {
2018-08-22 16:34:52 -05:00
ty ::FnDef ( .. ) | ty ::FnPtr ( _ ) = > {
2017-06-29 08:38:25 -05:00
let sig = typ . fn_sig ( self . cx . tcx ) ;
2021-10-07 04:21:30 -05:00
if self . cx . tcx . erase_late_bound_regions ( sig ) . output ( ) . kind ( ) = = & ty ::Never {
2016-12-20 11:21:30 -06:00
self . report_diverging_sub_expr ( e ) ;
}
} ,
_ = > { } ,
}
2016-09-06 08:15:36 -05:00
} ,
2018-07-12 02:30:57 -05:00
ExprKind ::MethodCall ( .. ) = > {
2020-07-17 03:47:04 -05:00
let borrowed_table = self . cx . typeck_results ( ) ;
2017-06-01 23:13:04 -05:00
if borrowed_table . expr_ty ( e ) . is_never ( ) {
2016-09-13 05:41:37 -05:00
self . report_diverging_sub_expr ( e ) ;
}
} ,
2016-09-13 05:41:20 -05:00
_ = > {
2017-08-09 02:30:56 -05:00
// do not lint expressions referencing objects of type `!`, as that required a
// diverging expression
2016-12-21 03:25:14 -06:00
// to begin with
2016-09-06 08:15:36 -05:00
} ,
}
self . maybe_walk_expr ( e ) ;
}
2019-12-27 01:12:26 -06:00
fn visit_block ( & mut self , _ : & ' tcx Block < '_ > ) {
2016-09-06 08:15:36 -05:00
// don't continue over blocks, LateLintPass already does that
}
2016-08-10 22:16:28 -05:00
}
2017-05-05 11:51:57 -05:00
/// Walks up the AST from the given write expression (`vis.write_expr`) looking
/// for reads to the same variable that are unsequenced relative to the write.
2016-08-10 22:16:28 -05:00
///
/// This means reads for which there is a common ancestor between the read and
/// the write such that
///
2018-11-27 14:14:15 -06:00
/// * evaluating the ancestor necessarily evaluates both the read and the write (for example, `&x`
/// and `|| x = 1` don't necessarily evaluate `x`), and
2016-08-10 22:16:28 -05:00
///
2018-11-27 14:14:15 -06:00
/// * which one is evaluated first depends on the order of sub-expression evaluation. Blocks, `if`s,
/// loops, `match`es, and the short-circuiting logical operators are considered to have a defined
/// evaluation order.
2016-08-10 22:16:28 -05:00
///
/// When such a read is found, the lint is triggered.
2018-07-23 06:01:12 -05:00
fn check_for_unsequenced_reads ( vis : & mut ReadVisitor < '_ , '_ > ) {
2018-12-07 18:56:03 -06:00
let map = & vis . cx . tcx . hir ( ) ;
2019-02-24 12:43:15 -06:00
let mut cur_id = vis . write_expr . hir_id ;
2016-08-10 22:16:28 -05:00
loop {
2019-06-25 16:33:51 -05:00
let parent_id = map . get_parent_node ( cur_id ) ;
2016-08-10 22:16:28 -05:00
if parent_id = = cur_id {
break ;
}
2022-10-10 15:37:42 -05:00
let Some ( parent_node ) = map . find ( parent_id ) else { break } ;
2016-08-10 22:16:28 -05:00
let stop_early = match parent_node {
2018-08-28 06:13:42 -05:00
Node ::Expr ( expr ) = > check_expr ( vis , expr ) ,
Node ::Stmt ( stmt ) = > check_stmt ( vis , stmt ) ,
Node ::Item ( _ ) = > {
2016-08-10 22:16:28 -05:00
// We reached the top of the function, stop.
break ;
} ,
2016-12-20 11:21:30 -06:00
_ = > StopEarly ::KeepGoing ,
2016-08-10 22:16:28 -05:00
} ;
match stop_early {
StopEarly ::Stop = > break ,
StopEarly ::KeepGoing = > { } ,
}
cur_id = parent_id ;
}
}
/// Whether to stop early for the loop in `check_for_unsequenced_reads`. (If
/// `check_expr` weren't an independent function, this would be unnecessary and
/// we could just use `break`).
enum StopEarly {
KeepGoing ,
Stop ,
}
2022-10-28 12:18:07 -05:00
fn check_expr < ' tcx > ( vis : & mut ReadVisitor < '_ , ' tcx > , expr : & ' tcx Expr < '_ > ) -> StopEarly {
2019-02-24 12:43:15 -06:00
if expr . hir_id = = vis . last_expr . hir_id {
2016-08-10 22:16:28 -05:00
return StopEarly ::KeepGoing ;
}
2019-09-27 10:16:06 -05:00
match expr . kind {
2018-11-27 14:14:15 -06:00
ExprKind ::Array ( _ )
| ExprKind ::Tup ( _ )
| ExprKind ::MethodCall ( .. )
| ExprKind ::Call ( _ , _ )
2019-12-23 22:16:04 -06:00
| ExprKind ::Assign ( .. )
2018-11-27 14:14:15 -06:00
| ExprKind ::Index ( _ , _ )
| ExprKind ::Repeat ( _ , _ )
| ExprKind ::Struct ( _ , _ , _ ) = > {
2016-08-10 22:16:28 -05:00
walk_expr ( vis , expr ) ;
2016-12-20 11:21:30 -06:00
} ,
2018-07-12 02:30:57 -05:00
ExprKind ::Binary ( op , _ , _ ) | ExprKind ::AssignOp ( op , _ , _ ) = > {
2018-07-12 02:50:09 -05:00
if op . node = = BinOpKind ::And | | op . node = = BinOpKind ::Or {
2016-08-10 22:16:28 -05:00
// x && y and x || y always evaluate x first, so these are
// strictly sequenced.
} else {
walk_expr ( vis , expr ) ;
}
2016-12-20 11:21:30 -06:00
} ,
2022-06-11 14:25:25 -05:00
ExprKind ::Closure { .. } = > {
2016-08-10 22:16:28 -05:00
// Either
//
2018-11-27 14:14:15 -06:00
// * `var` is defined in the closure body, in which case we've reached the top of the enclosing
// function and can stop, or
2016-08-10 22:16:28 -05:00
//
2018-11-27 14:14:15 -06:00
// * `var` is captured by the closure, in which case, because evaluating a closure does not evaluate
// its body, we don't necessarily have a write, so we need to stop to avoid generating false
// positives.
2016-08-10 22:16:28 -05:00
//
// This is also the only place we need to stop early (grrr).
return StopEarly ::Stop ;
2016-12-20 11:21:30 -06:00
} ,
2016-08-10 22:16:28 -05:00
// All other expressions either have only one child or strictly
// sequence the evaluation order of their sub-expressions.
2016-12-20 11:21:30 -06:00
_ = > { } ,
2016-08-10 22:16:28 -05:00
}
vis . last_expr = expr ;
StopEarly ::KeepGoing
}
2022-10-28 12:18:07 -05:00
fn check_stmt < ' tcx > ( vis : & mut ReadVisitor < '_ , ' tcx > , stmt : & ' tcx Stmt < '_ > ) -> StopEarly {
2019-09-27 10:16:06 -05:00
match stmt . kind {
2021-04-08 10:50:13 -05:00
StmtKind ::Expr ( expr ) | StmtKind ::Semi ( expr ) = > check_expr ( vis , expr ) ,
2019-01-20 04:21:30 -06:00
// If the declaration is of a local variable, check its initializer
// expression if it has one. Otherwise, keep going.
2021-04-08 10:50:13 -05:00
StmtKind ::Local ( local ) = > local
2019-01-20 04:49:45 -06:00
. init
. as_ref ( )
. map_or ( StopEarly ::KeepGoing , | expr | check_expr ( vis , expr ) ) ,
2021-03-25 13:29:11 -05:00
StmtKind ::Item ( .. ) = > StopEarly ::KeepGoing ,
2016-08-10 22:16:28 -05:00
}
}
/// A visitor that looks for reads from a variable.
2019-06-19 13:36:23 -05:00
struct ReadVisitor < ' a , ' tcx > {
2020-06-25 15:41:36 -05:00
cx : & ' a LateContext < ' tcx > ,
2019-01-30 19:15:29 -06:00
/// The ID of the variable we're looking for.
2019-04-14 15:18:34 -05:00
var : HirId ,
2016-08-10 22:16:28 -05:00
/// The expressions where the write to the variable occurred (for reporting
/// in the lint).
2019-12-27 01:12:26 -06:00
write_expr : & ' tcx Expr < ' tcx > ,
2016-08-10 22:16:28 -05:00
/// The last (highest in the AST) expression we've checked, so we know not
/// to recheck it.
2019-12-27 01:12:26 -06:00
last_expr : & ' tcx Expr < ' tcx > ,
2016-08-10 22:16:28 -05:00
}
2016-12-06 04:32:21 -06:00
impl < ' a , ' tcx > Visitor < ' tcx > for ReadVisitor < ' a , ' tcx > {
2019-12-27 01:12:26 -06:00
fn visit_expr ( & mut self , expr : & ' tcx Expr < '_ > ) {
2019-02-24 12:43:15 -06:00
if expr . hir_id = = self . last_expr . hir_id {
2016-08-10 22:16:28 -05:00
return ;
}
2021-02-11 08:04:38 -06:00
if path_to_local_id ( expr , self . var ) {
// Check that this is a read, not a write.
if ! is_in_assignment_position ( self . cx , expr ) {
span_lint_and_note (
self . cx ,
2022-05-21 06:24:00 -05:00
MIXED_READ_WRITE_IN_EXPRESSION ,
2021-02-11 08:04:38 -06:00
expr . span ,
2021-05-06 04:51:22 -05:00
& format! ( " unsequenced read of ` {} ` " , self . cx . tcx . hir ( ) . name ( self . var ) ) ,
2021-02-11 08:04:38 -06:00
Some ( self . write_expr . span ) ,
" whether read occurs before this write depends on evaluation order " ,
) ;
2016-08-10 22:16:28 -05:00
}
2021-02-11 08:04:38 -06:00
}
match expr . kind {
2016-08-10 22:16:28 -05:00
// We're about to descend a closure. Since we don't know when (or
// if) the closure will be evaluated, any reads in it might not
// occur here (or ever). Like above, bail to avoid false positives.
2022-06-11 14:25:25 -05:00
ExprKind ::Closure { .. } |
2016-08-10 22:16:28 -05:00
// We want to avoid a false positive when a variable name occurs
// only to have its address taken, so we stop here. Technically,
// this misses some weird cases, eg.
//
// ```rust
// let mut x = 0;
// let a = foo(&{x = 1; x}, x);
// ```
//
// TODO: fix this
2019-11-27 16:34:32 -06:00
ExprKind ::AddrOf ( _ , _ , _ ) = > {
2016-08-10 22:16:28 -05:00
return ;
}
_ = > { }
}
walk_expr ( self , expr ) ;
}
}
2019-01-30 19:15:29 -06:00
/// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`.
2020-06-25 15:41:36 -05:00
fn is_in_assignment_position ( cx : & LateContext < '_ > , expr : & Expr < '_ > ) -> bool {
2016-08-10 22:16:28 -05:00
if let Some ( parent ) = get_parent_expr ( cx , expr ) {
2021-04-08 10:50:13 -05:00
if let ExprKind ::Assign ( lhs , .. ) = parent . kind {
2019-02-24 12:43:15 -06:00
return lhs . hir_id = = expr . hir_id ;
2016-08-10 22:16:28 -05:00
}
}
false
}