// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 -verify %s
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s

void clang_analyzer_eval(bool);

struct S{
    static int CtorInvocationCount;
    static int DtorInvocationCount;

    S(){CtorInvocationCount++;}
    ~S(){DtorInvocationCount++;}
};

int S::CtorInvocationCount = 0;
int S::DtorInvocationCount = 0;

void zeroSizeArrayStack() {
    S::CtorInvocationCount = 0;

    S arr[0];

    clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
}

void zeroSizeMultidimensionalArrayStack() {
    S::CtorInvocationCount = 0;
    S::DtorInvocationCount = 0;

    {
        S arr[2][0];
        S arr2[0][2];

        S arr3[0][2][2];
        S arr4[2][2][0];
        S arr5[2][0][2];
    }

    clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
    clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}}
}

void zeroSizeArrayStackInLambda() {
    S::CtorInvocationCount = 0;
    S::DtorInvocationCount = 0;

    []{
        S arr[0];
    }();    
    
    clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
    clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}}
}

void zeroSizeArrayHeap() {
    S::CtorInvocationCount = 0;
    S::DtorInvocationCount = 0;

    auto *arr = new S[0];
    delete[] arr;
    
    clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
    clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}}
}

void zeroSizeMultidimensionalArrayHeap() {
    S::CtorInvocationCount = 0;
    S::DtorInvocationCount = 0;

    auto *arr = new S[2][0];
    delete[] arr;
        
    auto *arr2 = new S[0][2];
    delete[] arr2;

    auto *arr3 = new S[0][2][2];
    delete[] arr3;

    auto *arr4 = new S[2][2][0];
    delete[] arr4;

    auto *arr5 = new S[2][0][2];
    delete[] arr5;

    clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
    clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}}
}

#if __cplusplus >= 201703L

void zeroSizeArrayBinding() {
    S::CtorInvocationCount = 0;

    S arr[0];

    // Note: This is an error in gcc but a warning in clang.
    // In MSVC the declaration of 'S arr[0]' is already an error
    // and it doesn't recognize this syntax as a structured binding.
    auto [] = arr; //expected-warning{{ISO C++17 does not allow a decomposition group to be empty}}

    clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
}

#endif

void zeroSizeArrayLambdaCapture() {
    S::CtorInvocationCount = 0;
    S::DtorInvocationCount = 0;
        
    S arr[0];

    auto l = [arr]{};
    [arr]{}();    
    
    //FIXME: These should be TRUE. We should avoid calling the destructor 
    // of the temporary that is materialized as the lambda.
    clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}} expected-warning{{FALSE}}
    clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}} expected-warning{{FALSE}}
}

// FIXME: Report a warning if the standard is at least C++17.
#if __cplusplus < 201703L
void zeroSizeArrayLambdaCaptureUndefined1() {
    S arr[0];
    int n;

    auto l = [arr, n]{
        int x = n; //expected-warning{{Assigned value is garbage or undefined}}
        (void) x;
    };

    l();
}
#endif

void zeroSizeArrayLambdaCaptureUndefined2() {
    S arr[0];
    int n;

    [arr, n]{
        int x = n; //expected-warning{{Assigned value is garbage or undefined}}
        (void) x;
    }();
}

struct Wrapper{
    S arr[0];
};

void zeroSizeArrayMember() {
    S::CtorInvocationCount = 0;
    S::DtorInvocationCount = 0;

    {
        Wrapper W;
    }

    clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
    clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}}
}

void zeroSizeArrayMemberCopyMove() {
    S::CtorInvocationCount = 0;
    S::DtorInvocationCount = 0;
    
    {
        Wrapper W;
        Wrapper W2 = W;
        Wrapper W3 = (Wrapper&&) W2;
    }

    clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
    clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}}
}

struct MultiWrapper{
    S arr[2][0];
};

void zeroSizeMultidimensionalArrayMember() {
    S::CtorInvocationCount = 0;
    S::DtorInvocationCount = 0;

    {
        MultiWrapper MW;
    }

    clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}}
    clang_analyzer_eval(S::DtorInvocationCount == 0); //expected-warning{{TRUE}}
}
