#![warn(clippy::const_is_empty)]
#![allow(clippy::needless_late_init, unused_must_use)]

fn test_literal() {
    if "".is_empty() {
        //~^ERROR: this expression always evaluates to true
    }
    if "foobar".is_empty() {
        //~^ERROR: this expression always evaluates to false
    }
}

fn test_byte_literal() {
    if b"".is_empty() {
        //~^ERROR: this expression always evaluates to true
    }
    if b"foobar".is_empty() {
        //~^ERROR: this expression always evaluates to false
    }
}

fn test_no_mut() {
    let mut empty = "";
    if empty.is_empty() {
        // No lint because it is mutable
    }
}

fn test_propagated() {
    let empty = "";
    let non_empty = "foobar";
    let empty2 = empty;
    let non_empty2 = non_empty;
    if empty2.is_empty() {
        //~^ERROR: this expression always evaluates to true
    }
    if non_empty2.is_empty() {
        //~^ERROR: this expression always evaluates to false
    }
}

const EMPTY_STR: &str = "";
const NON_EMPTY_STR: &str = "foo";
const EMPTY_BSTR: &[u8] = b"";
const NON_EMPTY_BSTR: &[u8] = b"foo";
const EMPTY_U8_SLICE: &[u8] = &[];
const NON_EMPTY_U8_SLICE: &[u8] = &[1, 2];
const EMPTY_SLICE: &[u32] = &[];
const NON_EMPTY_SLICE: &[u32] = &[1, 2];
const NON_EMPTY_SLICE_REPEAT: &[u32] = &[1; 2];
const EMPTY_ARRAY: [u32; 0] = [];
const EMPTY_ARRAY_REPEAT: [u32; 0] = [1; 0];
const NON_EMPTY_ARRAY: [u32; 2] = [1, 2];
const NON_EMPTY_ARRAY_REPEAT: [u32; 2] = [1; 2];
const EMPTY_REF_ARRAY: &[u32; 0] = &[];
const NON_EMPTY_REF_ARRAY: &[u32; 3] = &[1, 2, 3];

fn test_from_const() {
    let _ = EMPTY_STR.is_empty();
    //~^ ERROR: this expression always evaluates to true
    let _ = NON_EMPTY_STR.is_empty();
    //~^ ERROR: this expression always evaluates to false
    let _ = EMPTY_BSTR.is_empty();
    //~^ ERROR: this expression always evaluates to true
    let _ = NON_EMPTY_BSTR.is_empty();
    //~^ ERROR: this expression always evaluates to false
    let _ = EMPTY_ARRAY.is_empty();
    //~^ ERROR: this expression always evaluates to true
    let _ = EMPTY_ARRAY_REPEAT.is_empty();
    //~^ ERROR: this expression always evaluates to true
    let _ = EMPTY_U8_SLICE.is_empty();
    //~^ ERROR: this expression always evaluates to true
    let _ = NON_EMPTY_U8_SLICE.is_empty();
    //~^ ERROR: this expression always evaluates to false
    let _ = NON_EMPTY_ARRAY.is_empty();
    //~^ ERROR: this expression always evaluates to false
    let _ = NON_EMPTY_ARRAY_REPEAT.is_empty();
    //~^ ERROR: this expression always evaluates to false
    let _ = EMPTY_REF_ARRAY.is_empty();
    //~^ ERROR: this expression always evaluates to true
    let _ = NON_EMPTY_REF_ARRAY.is_empty();
    //~^ ERROR: this expression always evaluates to false
    let _ = EMPTY_SLICE.is_empty();
    //~^ ERROR: this expression always evaluates to true
    let _ = NON_EMPTY_SLICE.is_empty();
    //~^ ERROR: this expression always evaluates to false
    let _ = NON_EMPTY_SLICE_REPEAT.is_empty();
    //~^ ERROR: this expression always evaluates to false
}

fn main() {
    let value = "foobar";
    let _ = value.is_empty();
    //~^ ERROR: this expression always evaluates to false
    let x = value;
    let _ = x.is_empty();
    //~^ ERROR: this expression always evaluates to false
    let _ = "".is_empty();
    //~^ ERROR: this expression always evaluates to true
    let _ = b"".is_empty();
    //~^ ERROR: this expression always evaluates to true
}

fn str_from_arg(var: &str) {
    var.is_empty();
    // Do not lint, we know nothiny about var
}

fn update_str() {
    let mut value = "duck";
    value = "penguin";

    let _ = value.is_empty();
    // Do not lint since value is mutable
}

fn macros() {
    // Content from Macro
    let file = include_str!("const_is_empty.rs");
    let _ = file.is_empty();
    // No lint because initializer comes from a macro result

    let var = env!("PATH");
    let _ = var.is_empty();
    // No lint because initializer comes from a macro result
}

fn conditional_value() {
    let value;

    if true {
        value = "hey";
    } else {
        value = "hej";
    }

    let _ = value.is_empty();
    // Do not lint, current constant folding is too simple to detect this
}

fn cfg_conditioned() {
    #[cfg(test)]
    let val = "";
    #[cfg(not(test))]
    let val = "foo";

    let _ = val.is_empty();
    // Do not lint, value depend on a #[cfg(…)] directive
}

fn not_cfg_conditioned() {
    let val = "";
    #[cfg(not(target_os = "inexistent"))]
    let _ = val.is_empty();
    //~^ ERROR: this expression always evaluates to true
}

const fn const_rand() -> &'static str {
    "17"
}

fn const_expressions() {
    let _ = const { if true { "1" } else { "2" } }.is_empty();
    // Do not lint, we do not recurse into boolean expressions

    let _ = const_rand().is_empty();
    // Do not lint, we do not recurse into functions
}

fn constant_from_external_crate() {
    let _ = std::env::consts::EXE_EXTENSION.is_empty();
    // Do not lint, `exe_ext` comes from the `std` crate
}