394 lines
6.7 KiB
Rust
394 lines
6.7 KiB
Rust
//@no-rustfix
|
|
//@aux-build:proc_macros.rs
|
|
|
|
#![allow(clippy::never_loop)]
|
|
#![warn(clippy::infinite_loop)]
|
|
|
|
extern crate proc_macros;
|
|
use proc_macros::{external, with_span};
|
|
|
|
fn do_something() {}
|
|
|
|
fn no_break() {
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
do_something();
|
|
}
|
|
}
|
|
|
|
fn all_inf() {
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
do_something();
|
|
}
|
|
}
|
|
do_something();
|
|
}
|
|
}
|
|
|
|
fn no_break_return_some_ty() -> Option<u8> {
|
|
loop {
|
|
do_something();
|
|
return None;
|
|
}
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
do_something();
|
|
}
|
|
}
|
|
|
|
fn no_break_never_ret() -> ! {
|
|
loop {
|
|
do_something();
|
|
}
|
|
}
|
|
|
|
fn no_break_never_ret_noise() {
|
|
loop {
|
|
fn inner_fn() -> ! {
|
|
std::process::exit(0);
|
|
}
|
|
do_something();
|
|
}
|
|
}
|
|
|
|
fn has_direct_break_1() {
|
|
loop {
|
|
do_something();
|
|
break;
|
|
}
|
|
}
|
|
|
|
fn has_direct_break_2() {
|
|
'outer: loop {
|
|
do_something();
|
|
break 'outer;
|
|
}
|
|
}
|
|
|
|
fn has_indirect_break_1(cond: bool) {
|
|
'outer: loop {
|
|
loop {
|
|
if cond {
|
|
break 'outer;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn has_indirect_break_2(stop_num: i32) {
|
|
'outer: loop {
|
|
for x in 0..5 {
|
|
if x == stop_num {
|
|
break 'outer;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn break_inner_but_not_outer_1(cond: bool) {
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
loop {
|
|
if cond {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn break_inner_but_not_outer_2(cond: bool) {
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
'inner: loop {
|
|
loop {
|
|
if cond {
|
|
break 'inner;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn break_outer_but_not_inner() {
|
|
loop {
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
do_something();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
fn can_break_both_inner_and_outer(cond: bool) {
|
|
'outer: loop {
|
|
loop {
|
|
if cond {
|
|
break 'outer;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn break_wrong_loop(cond: bool) {
|
|
// 'inner has statement to break 'outer loop, but it was breaked early by a labeled child loop
|
|
'outer: loop {
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
'inner: loop {
|
|
loop {
|
|
loop {
|
|
break 'inner;
|
|
}
|
|
break 'outer;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn has_direct_return(cond: bool) {
|
|
loop {
|
|
if cond {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn ret_in_inner(cond: bool) {
|
|
loop {
|
|
loop {
|
|
if cond {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
enum Foo {
|
|
A,
|
|
B,
|
|
C,
|
|
}
|
|
|
|
fn match_like() {
|
|
let opt: Option<u8> = Some(1);
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
match opt {
|
|
Some(v) => {
|
|
println!("{v}");
|
|
},
|
|
None => {
|
|
do_something();
|
|
},
|
|
}
|
|
}
|
|
|
|
loop {
|
|
match opt {
|
|
Some(v) => {
|
|
println!("{v}");
|
|
},
|
|
None => {
|
|
do_something();
|
|
break;
|
|
},
|
|
}
|
|
}
|
|
|
|
let result: Result<u8, u16> = Ok(1);
|
|
loop {
|
|
let _val = match result {
|
|
Ok(1) => 1 + 1,
|
|
Ok(v) => v / 2,
|
|
Err(_) => return,
|
|
};
|
|
}
|
|
|
|
loop {
|
|
let Ok(_val) = result else { return };
|
|
}
|
|
|
|
loop {
|
|
let Ok(_val) = result.map(|v| 10) else { break };
|
|
}
|
|
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
let _x = matches!(result, Ok(v) if v != 0).then_some(0);
|
|
}
|
|
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
// This `return` does not return the function, so it doesn't count
|
|
let _x = matches!(result, Ok(v) if v != 0).then(|| {
|
|
if true {
|
|
return;
|
|
}
|
|
do_something();
|
|
});
|
|
}
|
|
|
|
let mut val = 0;
|
|
let mut fooc = Foo::C;
|
|
|
|
loop {
|
|
val = match fooc {
|
|
Foo::A => 0,
|
|
Foo::B => {
|
|
fooc = Foo::C;
|
|
1
|
|
},
|
|
Foo::C => break,
|
|
};
|
|
}
|
|
|
|
loop {
|
|
val = match fooc {
|
|
Foo::A => 0,
|
|
Foo::B => 1,
|
|
Foo::C => {
|
|
break;
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
macro_rules! set_or_ret {
|
|
($opt:expr, $a:expr) => {{
|
|
match $opt {
|
|
Some(val) => $a = val,
|
|
None => return,
|
|
}
|
|
}};
|
|
}
|
|
|
|
fn ret_in_macro(opt: Option<u8>) {
|
|
let opt: Option<u8> = Some(1);
|
|
let mut a: u8 = 0;
|
|
loop {
|
|
set_or_ret!(opt, a);
|
|
}
|
|
|
|
let res: Result<bool, u8> = Ok(true);
|
|
loop {
|
|
match res {
|
|
Ok(true) => set_or_ret!(opt, a),
|
|
_ => do_something(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn panic_like_macros_1() {
|
|
loop {
|
|
do_something();
|
|
panic!();
|
|
}
|
|
}
|
|
|
|
fn panic_like_macros_2() {
|
|
let mut x = 0;
|
|
|
|
loop {
|
|
do_something();
|
|
if true {
|
|
todo!();
|
|
}
|
|
}
|
|
loop {
|
|
do_something();
|
|
x += 1;
|
|
assert_eq!(x, 0);
|
|
}
|
|
loop {
|
|
do_something();
|
|
assert!(x % 2 == 0);
|
|
}
|
|
loop {
|
|
do_something();
|
|
match Some(1) {
|
|
Some(n) => println!("{n}"),
|
|
None => unreachable!("It won't happen"),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn exit_directly(cond: bool) {
|
|
loop {
|
|
if cond {
|
|
std::process::exit(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
trait MyTrait {
|
|
fn problematic_trait_method() {
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
do_something();
|
|
}
|
|
}
|
|
fn could_be_problematic();
|
|
}
|
|
|
|
impl MyTrait for String {
|
|
fn could_be_problematic() {
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
do_something();
|
|
}
|
|
}
|
|
}
|
|
|
|
fn inf_loop_in_closure() {
|
|
let _loop_forever = || {
|
|
loop {
|
|
//~^ ERROR: infinite loop detected
|
|
do_something();
|
|
}
|
|
};
|
|
|
|
let _somehow_ok = || -> ! {
|
|
loop {
|
|
do_something();
|
|
}
|
|
};
|
|
}
|
|
|
|
fn inf_loop_in_res() -> Result<(), i32> {
|
|
Ok(loop {
|
|
do_something()
|
|
})
|
|
}
|
|
|
|
with_span! { span
|
|
fn no_loop() {}
|
|
}
|
|
|
|
with_span! { span
|
|
fn with_loop() {
|
|
loop {
|
|
do_nothing();
|
|
}
|
|
}
|
|
}
|
|
|
|
fn do_nothing() {}
|
|
|
|
fn span_inside_fn() {
|
|
with_span! { span
|
|
loop {
|
|
do_nothing();
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() {}
|