Skip to content

False positive for break_with_label_and_loop lint & incorrect suggestion #137414

Closed
@wmmc88

Description

@wmmc88

I tried this code:

use std::mem::MaybeUninit;

#[derive(Debug)]
pub struct Foo {
    pub field_1: u32,
    pub field_2: u64,
}

const SUCCESS: u32 = 0;

unsafe fn ffi_init_foo(foo: *mut Foo) -> u32 {
    (*foo).field_1 = 1;
    (*foo).field_2 = 2;
    SUCCESS
}

fn func_1() -> Result<Foo, u32>{
    let mut foo = MaybeUninit::<Foo>::uninit();

    let foo = 'block: {
        let ret_val = unsafe {
            ffi_init_foo(foo.as_mut_ptr())
        };
        if ret_val == SUCCESS {
            break 'block unsafe { foo.assume_init() };
        }
        eprintln!("ERROR: {ret_val}");
        return Err(ret_val);
    };
    
    Ok(foo)
}

// uses compiler suggestion!
fn func_2() -> Result<Foo, u32>{
    let mut foo = MaybeUninit::<Foo>::uninit();

    let foo = 'block: {
        let ret_val = unsafe {
            ffi_init_foo(foo.as_mut_ptr())
        };
        if ret_val == SUCCESS {
            break 'block (unsafe { foo.assume_init() });
        }
        eprintln!("ERROR: {ret_val}");
        return Err(ret_val);
    };
    
    Ok(foo)
}

// this works without warnings
fn func_3() -> Result<Foo, u32>{
    let mut foo = MaybeUninit::<Foo>::uninit();

    let foo = 'block: {
        let ret_val = unsafe {
            ffi_init_foo(foo.as_mut_ptr())
        };
        if ret_val == SUCCESS {
            let foo = unsafe { foo.assume_init() };
            break 'block foo;
        }
        eprintln!("ERROR: {ret_val}");
        return Err(ret_val);
    };
    
    Ok(foo)
}

fn main() {
    let _ = dbg!(func_1());
    let _ = dbg!(func_2());
    let _ = dbg!(func_3());
}

I expected func_1 to compile without warning but it produced:

warning: this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression
  --> src/main.rs:25:13
   |
25 |             break 'block unsafe { foo.assume_init() };
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(break_with_label_and_loop)]` on by default
help: wrap this expression in parentheses
   |
25 |             break 'block (unsafe { foo.assume_init() });
   |                          +                            +

func_2 is the same as func_1, but it follows the compilers suggestion and produces this warning:

warning: unnecessary parentheses around `break` value
  --> src/main.rs:43:26
   |
43 |             break 'block (unsafe { foo.assume_init() });
   |                          ^                            ^
   |
   = note: `#[warn(unused_parens)]` on by default
help: remove these parentheses
   |
43 -             break 'block (unsafe { foo.assume_init() });
43 +             break 'block unsafe { foo.assume_init() };
   |

func_3 compiles without warnings, but it adds a needless assignment.

Meta

rustc --version --verbose:

1.87.0-nightly

(2025-02-21 794c12416b2138064af1)

The issue is also present on latest stable (1.85)

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.L-break_with_label_and_loopLint: break_with_label_and_loopL-false-positiveLint: False positive (should not have fired).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

    No branches or pull requests

    Issue actions