2021-03-25 18:29:11 +00: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 09:51:22 +00:00
use if_chain ::if_chain ;
2020-01-09 07:13:22 +00:00
use rustc_hir ::intravisit ::{ walk_expr , NestedVisitorMap , Visitor } ;
2021-02-11 14:04:38 +00:00
use rustc_hir ::{ BinOpKind , Block , Expr , ExprKind , Guard , HirId , Local , Node , Stmt , StmtKind } ;
2020-01-12 06:08:41 +00:00
use rustc_lint ::{ LateContext , LateLintPass } ;
2020-03-30 09:02:14 +00:00
use rustc_middle ::hir ::map ::Map ;
use rustc_middle ::ty ;
2020-01-11 11:37:08 +00:00
use rustc_session ::{ declare_lint_pass , declare_tool_lint } ;
2016-08-11 03:16:28 +00:00
2018-03-28 13:24:26 +00:00
declare_clippy_lint! {
2021-07-29 10:16:06 +00:00
/// ### What it does
/// Checks for a read and a write to the same variable where
2019-03-05 16:50:33 +00:00
/// whether the read occurs before or after the write depends on the evaluation
/// order of sub-expressions.
///
2021-07-29 10:16:06 +00:00
/// ### Why is this bad?
2021-09-08 14:31:47 +00: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 16:50:33 +00:00
///
2021-07-29 10:16:06 +00:00
/// ### Known problems
/// Code which intentionally depends on the evaluation
2019-03-05 16:50:33 +00:00
/// order, or which is correct for any evaluation order.
///
2021-07-29 10:16:06 +00:00
/// ### Example
2019-03-05 16:50:33 +00:00
/// ```rust
/// let mut x = 0;
2020-06-09 14:36:01 +00:00
///
/// // Bad
2019-03-05 16:50:33 +00:00
/// let a = {
/// x = 1;
/// 1
/// } + x;
/// // Unclear whether a is 1 or 2.
2020-06-09 14:36:01 +00:00
///
/// // Good
/// let tmp = {
/// x = 1;
/// 1
/// };
/// let a = tmp + x;
2019-03-05 16:50:33 +00:00
/// ```
2016-08-11 03:16:28 +00:00
pub EVAL_ORDER_DEPENDENCE ,
2021-07-01 16:17:38 +00:00
suspicious ,
2016-08-11 03:16:28 +00:00
" whether a variable read occurs before a write depends on sub-expression evaluation order "
}
2018-03-28 13:24:26 +00:00
declare_clippy_lint! {
2021-07-29 10:16:06 +00:00
/// ### What it does
/// Checks for diverging calls that are not match arms or
2019-03-05 16:50:33 +00:00
/// statements.
///
2021-07-29 10:16:06 +00:00
/// ### Why is this bad?
/// It is often confusing to read. In addition, the
2019-03-05 16:50:33 +00:00
/// sub-expression evaluation order for Rust is not well documented.
///
2021-07-29 10:16:06 +00:00
/// ### Known problems
/// Someone might want to use `some_bool || panic!()` as a
2019-03-05 16:50:33 +00:00
/// shorthand.
///
2021-07-29 10:16:06 +00:00
/// ### Example
2019-08-02 06:13:54 +00:00
/// ```rust,no_run
/// # fn b() -> bool { true }
/// # fn c() -> bool { true }
2019-03-05 16:50:33 +00: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!()`
/// ```
2016-09-06 13:15:36 +00:00
pub DIVERGING_SUB_EXPRESSION ,
2018-03-28 13:24:26 +00:00
complexity ,
2016-09-06 13:15:36 +00:00
" whether an expression contains a diverging sub expression "
}
2019-04-08 20:43:55 +00:00
declare_lint_pass! ( EvalOrderDependence = > [ EVAL_ORDER_DEPENDENCE , DIVERGING_SUB_EXPRESSION ] ) ;
2016-08-11 03:16:28 +00:00
2020-06-25 20:41:36 +00:00
impl < ' tcx > LateLintPass < ' tcx > for EvalOrderDependence {
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < '_ > ) {
2016-08-11 03:16:28 +00:00
// Find a write to a local variable.
2021-05-06 09:51:22 +00: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-11 03:16:28 +00:00
}
2020-06-25 20:41:36 +00:00
fn check_stmt ( & mut self , cx : & LateContext < ' tcx > , stmt : & ' tcx Stmt < '_ > ) {
2019-09-27 15:16:06 +00:00
match stmt . kind {
2021-04-08 15:50:13 +00:00
StmtKind ::Local ( local ) = > {
if let Local { init : Some ( e ) , .. } = local {
2019-01-20 10:49:45 +00:00
DivergenceVisitor { cx } . visit_expr ( e ) ;
}
2016-09-06 13:15:36 +00:00
} ,
2021-04-08 15:50:13 +00:00
StmtKind ::Expr ( e ) | StmtKind ::Semi ( e ) = > DivergenceVisitor { cx } . maybe_walk_expr ( e ) ,
2019-01-20 10:21:30 +00:00
StmtKind ::Item ( .. ) = > { } ,
2016-09-06 13:15:36 +00:00
}
}
}
2019-06-19 18:36:23 +00:00
struct DivergenceVisitor < ' a , ' tcx > {
2020-06-25 20:41:36 +00:00
cx : & ' a LateContext < ' tcx > ,
2016-12-06 10:32:21 +00:00
}
2016-09-06 13:15:36 +00:00
impl < ' a , ' tcx > DivergenceVisitor < ' a , ' tcx > {
2019-12-27 07:12:26 +00:00
fn maybe_walk_expr ( & mut self , e : & ' tcx Expr < '_ > ) {
2019-09-27 15:16:06 +00:00
match e . kind {
2019-09-12 06:36:05 +00:00
ExprKind ::Closure ( .. ) = > { } ,
2021-04-08 15:50:13 +00:00
ExprKind ::Match ( e , arms , _ ) = > {
2016-09-06 13:15:36 +00:00
self . visit_expr ( e ) ;
for arm in arms {
2020-03-16 06:23:03 +00:00
if let Some ( Guard ::If ( if_expr ) ) = arm . guard {
2021-06-03 06:41:37 +00:00
self . visit_expr ( if_expr ) ;
2016-09-06 13:15:36 +00:00
}
// make sure top level arm expressions aren't linted
2016-10-01 12:41:20 +00:00
self . maybe_walk_expr ( & * arm . body ) ;
2016-09-06 13:15:36 +00:00
}
2016-12-20 17:21:30 +00:00
} ,
2016-09-06 13:15:36 +00:00
_ = > walk_expr ( self , e ) ,
}
}
2019-12-27 07:12:26 +00:00
fn report_diverging_sub_expr ( & mut self , e : & Expr < '_ > ) {
2016-12-20 17:21:30 +00:00
span_lint ( self . cx , DIVERGING_SUB_EXPRESSION , e . span , " sub-expression diverges " ) ;
2016-09-06 13:15:36 +00:00
}
}
2016-12-06 10:32:21 +00:00
impl < ' a , ' tcx > Visitor < ' tcx > for DivergenceVisitor < ' a , ' tcx > {
2020-01-09 07:13:22 +00:00
type Map = Map < ' tcx > ;
2019-12-27 07:12:26 +00:00
fn visit_expr ( & mut self , e : & ' tcx Expr < '_ > ) {
2019-09-27 15:16:06 +00:00
match e . kind {
2018-07-12 07:30:57 +00:00
ExprKind ::Continue ( _ ) | ExprKind ::Break ( _ , _ ) | ExprKind ::Ret ( _ ) = > self . report_diverging_sub_expr ( e ) ,
2021-04-08 15:50:13 +00:00
ExprKind ::Call ( func , _ ) = > {
2020-07-17 08:47:04 +00:00
let typ = self . cx . typeck_results ( ) . expr_ty ( func ) ;
2020-08-03 22:18:29 +00:00
match typ . kind ( ) {
2018-08-22 21:34:52 +00:00
ty ::FnDef ( .. ) | ty ::FnPtr ( _ ) = > {
2017-06-29 13:38:25 +00:00
let sig = typ . fn_sig ( self . cx . tcx ) ;
2021-10-07 09:21:30 +00:00
if self . cx . tcx . erase_late_bound_regions ( sig ) . output ( ) . kind ( ) = = & ty ::Never {
2016-12-20 17:21:30 +00:00
self . report_diverging_sub_expr ( e ) ;
}
} ,
_ = > { } ,
}
2016-09-06 13:15:36 +00:00
} ,
2018-07-12 07:30:57 +00:00
ExprKind ::MethodCall ( .. ) = > {
2020-07-17 08:47:04 +00:00
let borrowed_table = self . cx . typeck_results ( ) ;
2017-06-02 04:13:04 +00:00
if borrowed_table . expr_ty ( e ) . is_never ( ) {
2016-09-13 10:41:37 +00:00
self . report_diverging_sub_expr ( e ) ;
}
} ,
2016-09-13 10:41:20 +00:00
_ = > {
2017-08-09 07:30:56 +00:00
// do not lint expressions referencing objects of type `!`, as that required a
// diverging expression
2016-12-21 09:25:14 +00:00
// to begin with
2016-09-06 13:15:36 +00:00
} ,
}
self . maybe_walk_expr ( e ) ;
}
2019-12-27 07:12:26 +00:00
fn visit_block ( & mut self , _ : & ' tcx Block < '_ > ) {
2016-09-06 13:15:36 +00:00
// don't continue over blocks, LateLintPass already does that
}
2020-03-15 22:41:20 +00:00
fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self ::Map > {
2017-05-12 10:02:42 +00:00
NestedVisitorMap ::None
2016-12-06 10:32:21 +00:00
}
2016-08-11 03:16:28 +00:00
}
2017-05-05 16:51:57 +00: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-11 03:16:28 +00:00
///
/// This means reads for which there is a common ancestor between the read and
/// the write such that
///
2018-11-27 20:14:15 +00: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-11 03:16:28 +00:00
///
2018-11-27 20:14:15 +00: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-11 03:16:28 +00:00
///
/// When such a read is found, the lint is triggered.
2018-07-23 11:01:12 +00:00
fn check_for_unsequenced_reads ( vis : & mut ReadVisitor < '_ , '_ > ) {
2018-12-08 00:56:03 +00:00
let map = & vis . cx . tcx . hir ( ) ;
2019-02-24 18:43:15 +00:00
let mut cur_id = vis . write_expr . hir_id ;
2016-08-11 03:16:28 +00:00
loop {
2019-06-25 21:33:51 +00:00
let parent_id = map . get_parent_node ( cur_id ) ;
2016-08-11 03:16:28 +00:00
if parent_id = = cur_id {
break ;
}
2019-06-25 21:34:07 +00:00
let parent_node = match map . find ( parent_id ) {
2016-08-11 03:16:28 +00:00
Some ( parent ) = > parent ,
None = > break ,
} ;
let stop_early = match parent_node {
2018-08-28 11:13:42 +00:00
Node ::Expr ( expr ) = > check_expr ( vis , expr ) ,
Node ::Stmt ( stmt ) = > check_stmt ( vis , stmt ) ,
Node ::Item ( _ ) = > {
2016-08-11 03:16:28 +00:00
// We reached the top of the function, stop.
break ;
} ,
2016-12-20 17:21:30 +00:00
_ = > StopEarly ::KeepGoing ,
2016-08-11 03:16:28 +00: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 ,
}
2019-12-27 07:12:26 +00:00
fn check_expr < ' a , ' tcx > ( vis : & mut ReadVisitor < ' a , ' tcx > , expr : & ' tcx Expr < '_ > ) -> StopEarly {
2019-02-24 18:43:15 +00:00
if expr . hir_id = = vis . last_expr . hir_id {
2016-08-11 03:16:28 +00:00
return StopEarly ::KeepGoing ;
}
2019-09-27 15:16:06 +00:00
match expr . kind {
2018-11-27 20:14:15 +00:00
ExprKind ::Array ( _ )
| ExprKind ::Tup ( _ )
| ExprKind ::MethodCall ( .. )
| ExprKind ::Call ( _ , _ )
2019-12-24 04:16:04 +00:00
| ExprKind ::Assign ( .. )
2018-11-27 20:14:15 +00:00
| ExprKind ::Index ( _ , _ )
| ExprKind ::Repeat ( _ , _ )
| ExprKind ::Struct ( _ , _ , _ ) = > {
2016-08-11 03:16:28 +00:00
walk_expr ( vis , expr ) ;
2016-12-20 17:21:30 +00:00
} ,
2018-07-12 07:30:57 +00:00
ExprKind ::Binary ( op , _ , _ ) | ExprKind ::AssignOp ( op , _ , _ ) = > {
2018-07-12 07:50:09 +00:00
if op . node = = BinOpKind ::And | | op . node = = BinOpKind ::Or {
2016-08-11 03:16:28 +00:00
// x && y and x || y always evaluate x first, so these are
// strictly sequenced.
} else {
walk_expr ( vis , expr ) ;
}
2016-12-20 17:21:30 +00:00
} ,
2018-07-12 07:30:57 +00:00
ExprKind ::Closure ( _ , _ , _ , _ , _ ) = > {
2016-08-11 03:16:28 +00:00
// Either
//
2018-11-27 20:14:15 +00: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-11 03:16:28 +00:00
//
2018-11-27 20:14:15 +00: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-11 03:16:28 +00:00
//
// This is also the only place we need to stop early (grrr).
return StopEarly ::Stop ;
2016-12-20 17:21:30 +00:00
} ,
2016-08-11 03:16:28 +00:00
// All other expressions either have only one child or strictly
// sequence the evaluation order of their sub-expressions.
2016-12-20 17:21:30 +00:00
_ = > { } ,
2016-08-11 03:16:28 +00:00
}
vis . last_expr = expr ;
StopEarly ::KeepGoing
}
2019-12-27 07:12:26 +00:00
fn check_stmt < ' a , ' tcx > ( vis : & mut ReadVisitor < ' a , ' tcx > , stmt : & ' tcx Stmt < '_ > ) -> StopEarly {
2019-09-27 15:16:06 +00:00
match stmt . kind {
2021-04-08 15:50:13 +00:00
StmtKind ::Expr ( expr ) | StmtKind ::Semi ( expr ) = > check_expr ( vis , expr ) ,
2019-01-20 10:21:30 +00:00
// If the declaration is of a local variable, check its initializer
// expression if it has one. Otherwise, keep going.
2021-04-08 15:50:13 +00:00
StmtKind ::Local ( local ) = > local
2019-01-20 10:49:45 +00:00
. init
. as_ref ( )
. map_or ( StopEarly ::KeepGoing , | expr | check_expr ( vis , expr ) ) ,
2021-03-25 18:29:11 +00:00
StmtKind ::Item ( .. ) = > StopEarly ::KeepGoing ,
2016-08-11 03:16:28 +00:00
}
}
/// A visitor that looks for reads from a variable.
2019-06-19 18:36:23 +00:00
struct ReadVisitor < ' a , ' tcx > {
2020-06-25 20:41:36 +00:00
cx : & ' a LateContext < ' tcx > ,
2019-01-31 01:15:29 +00:00
/// The ID of the variable we're looking for.
2019-04-14 20:18:34 +00:00
var : HirId ,
2016-08-11 03:16:28 +00:00
/// The expressions where the write to the variable occurred (for reporting
/// in the lint).
2019-12-27 07:12:26 +00:00
write_expr : & ' tcx Expr < ' tcx > ,
2016-08-11 03:16:28 +00:00
/// The last (highest in the AST) expression we've checked, so we know not
/// to recheck it.
2019-12-27 07:12:26 +00:00
last_expr : & ' tcx Expr < ' tcx > ,
2016-08-11 03:16:28 +00:00
}
2016-12-06 10:32:21 +00:00
impl < ' a , ' tcx > Visitor < ' tcx > for ReadVisitor < ' a , ' tcx > {
2020-01-09 07:13:22 +00:00
type Map = Map < ' tcx > ;
2019-12-27 07:12:26 +00:00
fn visit_expr ( & mut self , expr : & ' tcx Expr < '_ > ) {
2019-02-24 18:43:15 +00:00
if expr . hir_id = = self . last_expr . hir_id {
2016-08-11 03:16:28 +00:00
return ;
}
2021-02-11 14:04:38 +00: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 ,
EVAL_ORDER_DEPENDENCE ,
expr . span ,
2021-05-06 09:51:22 +00:00
& format! ( " unsequenced read of ` {} ` " , self . cx . tcx . hir ( ) . name ( self . var ) ) ,
2021-02-11 14:04:38 +00:00
Some ( self . write_expr . span ) ,
" whether read occurs before this write depends on evaluation order " ,
) ;
2016-08-11 03:16:28 +00:00
}
2021-02-11 14:04:38 +00:00
}
match expr . kind {
2016-08-11 03:16:28 +00: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.
2018-07-12 07:30:57 +00:00
ExprKind ::Closure ( _ , _ , _ , _ , _ ) |
2016-08-11 03:16:28 +00: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 22:34:32 +00:00
ExprKind ::AddrOf ( _ , _ , _ ) = > {
2016-08-11 03:16:28 +00:00
return ;
}
_ = > { }
}
walk_expr ( self , expr ) ;
}
2020-03-15 22:41:20 +00:00
fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self ::Map > {
2017-05-12 10:02:42 +00:00
NestedVisitorMap ::None
2016-12-06 10:32:21 +00:00
}
2016-08-11 03:16:28 +00:00
}
2019-01-31 01:15:29 +00:00
/// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`.
2020-06-25 20:41:36 +00:00
fn is_in_assignment_position ( cx : & LateContext < '_ > , expr : & Expr < '_ > ) -> bool {
2016-08-11 03:16:28 +00:00
if let Some ( parent ) = get_parent_expr ( cx , expr ) {
2021-04-08 15:50:13 +00:00
if let ExprKind ::Assign ( lhs , .. ) = parent . kind {
2019-02-24 18:43:15 +00:00
return lhs . hir_id = = expr . hir_id ;
2016-08-11 03:16:28 +00:00
}
}
false
}