Skip to content

False warning: "unused logical operation that must be used" #69466

@spacekookie

Description

@spacekookie
Member

In a loop, in some cases, rustc seems to not understand that a boolean expression was used for sideeffects, and warns about unused logical operation. The following code can quickly replicate the problem.

use std::sync::{atomic::{AtomicBool, Ordering}, Arc};


fn main() {
    let running = Arc::new(AtomicBool::from(true));
    
    {
        let running = Arc::clone(&running);
        std::thread::spawn(move || {
            running.swap(false, Ordering::Relaxed);
        });
    }
    
    loop {
        // ...
        println!("Running....");
        
        !running.load(Ordering::Relaxed) && break;
    }
    
    println!("Exhausted >.>");
}

The expected result is that rustc doesn't output a warning in this case.

Meta

  • rustc version: 1.41.0

Activity

RustyYato

RustyYato commented on Feb 25, 2020

@RustyYato
Contributor

I don't think this is the intended use of logical operators, you should use an if, when side effects matter. This lint should help people stay away from mis-using operators in this way.

added
A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
on Feb 25, 2020
nagisa

nagisa commented on Feb 25, 2020

@nagisa
Member

While it is a bad style to do something like this, it is also perfectly valid code. As rustc’s lints are expected to have no false-positives, this is a genuine bug.

The stylistic preference should be linted by clippy, not rustc.

Centril

Centril commented on Feb 25, 2020

@Centril
Contributor

For the record, I disagree that this is bad style, or it's at least debatable. It seems like it's taking advantage of the sugar that && brings over if, and Rust being a sideeffectful language, this should be expected.

As for whether this should be linted against or not, it seems to me it should not, but cc @rust-lang/lang if there are objections.

eddyb

eddyb commented on Feb 25, 2020

@eddyb
Member

I agree with @nagisa and @Centril, && and || should not be linted, they're control-flow operators, not value operators. Looks like operators like & and | also warn, so we can keep just those.

estebank

estebank commented on Mar 2, 2020

@estebank
Contributor

Pointers:

The code that needs to be modified is

fn check_stmt(&mut self, cx: &LateContext<'_, '_>, s: &hir::Stmt<'_>) {
let expr = match s.kind {
hir::StmtKind::Semi(ref expr) => &**expr,
_ => return,
};
if let hir::ExprKind::Ret(..) = expr.kind {
return;
}
let ty = cx.tables.expr_ty(&expr);
let type_permits_lack_of_use = check_must_use_ty(cx, ty, &expr, s.span, "", "", 1);
let mut fn_warned = false;
let mut op_warned = false;
let maybe_def_id = match expr.kind {
hir::ExprKind::Call(ref callee, _) => {
match callee.kind {
hir::ExprKind::Path(ref qpath) => {
match cx.tables.qpath_res(qpath, callee.hir_id) {
Res::Def(DefKind::Fn, def_id) | Res::Def(DefKind::Method, def_id) => {
Some(def_id)
}
// `Res::Local` if it was a closure, for which we
// do not currently support must-use linting
_ => None,
}
}
_ => None,
}
}
hir::ExprKind::MethodCall(..) => cx.tables.type_dependent_def_id(expr.hir_id),
_ => None,
};
if let Some(def_id) = maybe_def_id {
fn_warned = check_must_use_def(cx, def_id, s.span, "return value of ", "");
} else if type_permits_lack_of_use {
// We don't warn about unused unit or uninhabited types.
// (See https://github.com/rust-lang/rust/issues/43806 for details.)
return;
}
let must_use_op = match expr.kind {
// Hardcoding operators here seemed more expedient than the
// refactoring that would be needed to look up the `#[must_use]`
// attribute which does exist on the comparison trait methods
hir::ExprKind::Binary(bin_op, ..) => match bin_op.node {
hir::BinOpKind::Eq
| hir::BinOpKind::Lt
| hir::BinOpKind::Le
| hir::BinOpKind::Ne
| hir::BinOpKind::Ge
| hir::BinOpKind::Gt => Some("comparison"),
hir::BinOpKind::Add
| hir::BinOpKind::Sub
| hir::BinOpKind::Div
| hir::BinOpKind::Mul
| hir::BinOpKind::Rem => Some("arithmetic operation"),
hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"),
hir::BinOpKind::BitXor
| hir::BinOpKind::BitAnd
| hir::BinOpKind::BitOr
| hir::BinOpKind::Shl
| hir::BinOpKind::Shr => Some("bitwise operation"),
},
hir::ExprKind::Unary(..) => Some("unary operation"),
_ => None,
};
if let Some(must_use_op) = must_use_op {
cx.struct_span_lint(UNUSED_MUST_USE, expr.span, |lint| {
lint.build(&format!("unused {} that must be used", must_use_op)).emit()
});
op_warned = true;
}

I believe you will have to explore the ExprKind::Binary(_, _, rhs) recursively to see if the leaf rhs is of type !, and if that is the case, return early.

The cases where the lhs is ! should already be handled by the unreachable_code lint:

warning: unreachable expression
  --> src/main.rs:18:16
   |
18 |         break && break;
   |                ^^-----
   |                | |
   |                | any code following this expression is unreachable
   |                unreachable expression
   |
   = note: `#[warn(unreachable_code)]` on by default
eddyb

eddyb commented on Mar 2, 2020

@eddyb
Member

I believe you will have to explore the ExprKind::Binary(_, _, rhs) recursively to see if the leaf rhs is of type !, and if that is the case, return early.

I would just replace Some(...) with None on this line:

hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"),

As to lint correctly you'd probably also have to look at whether there any side-effects in the RHS.

spacekookie

spacekookie commented on Mar 4, 2020

@spacekookie
MemberAuthor

@estebank thanks! I'll try to have a look at this and post if I have any questions :)

lolbinarycat

lolbinarycat commented on Sep 5, 2024

@lolbinarycat
Contributor

triage: still an issue on latest nightly

TilmanR

TilmanR commented on May 24, 2025

@TilmanR

I'm new to rust and a bit weird when it comes to styling, so I just ran into this issue while trying to make my early returns look distinct from "regular" if statements. (This helps me keep track of guard clauses.)

fn example(input: u16) -> u8
{
    input < 37 &&
        return 0;// unused_must_use

    let _ = input > 42 &&
        return 13;// sorry
    
    return 42;
}

In such an extreme case the intention of a discarded result seems sufficiently clear. A fix for this would be marvelous.

Edit:
I took a look at the mentioned function on master, to see if I can figure out how to fix this. It's uncomfortably long and seems like a nightmare to maintain.

Pointers:

The code that needs to be modified is

rust/src/librustc_lint/unused.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.C-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @eddyb@nagisa@Centril@estebank@spacekookie

      Issue actions

        False warning: "unused logical operation that must be used" · Issue #69466 · rust-lang/rust