Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -590,6 +590,28 @@ unsigned Idx = 0; if (CE->getType()->isArrayType() || AILE) { + + auto isZeroSizeArray = [&] { + auto Size = 1; + + if (const auto *CAT = dyn_cast(CE->getType())) + Size = getContext().getConstantArrayElementCount(CAT); + + if (AILE) + Size = AILE->getArraySize().getLimitedValue(); + + return Size == 0; + }; + + // No element construction will happen in a 0 size array. + if (isZeroSizeArray()) { + StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); + static SimpleProgramPointTag T{"ExprEngine", + "Skipping 0 size array construction"}; + Bldr.generateNode(CE, Pred, State, &T); + return; + } + Idx = getIndexOfElementToConstruct(State, CE, LCtx).value_or(0u); State = setIndexOfElementToConstruct(State, CE, LCtx, Idx + 1); } @@ -1163,10 +1185,17 @@ "Extracted capture initialization expression is missing"); if (dyn_cast(InitExpr)) { - InitVal = *getObjectUnderConstruction(State, {LE, Idx}, LocCtxt); - InitVal = State->getSVal(InitVal.getAsRegion()); + // The condition is false if we capture a 0 size array. We do nothing in + // that case. + if (auto ObjVal = + getObjectUnderConstruction(State, {LE, Idx}, LocCtxt)) { + InitVal = State->getSVal(ObjVal->getAsRegion()); + + State = finishObjectConstruction(State, {LE, Idx}, LocCtxt); + } else { + InitVal = UndefinedVal{}; + } - State = finishObjectConstruction(State, {LE, Idx}, LocCtxt); } else InitVal = State->getSVal(InitExpr, LocCtxt); @@ -1182,7 +1211,8 @@ InitVal = State->getSVal(SizeExpr, LocCtxt); } - State = State->bindLoc(FieldLoc, InitVal, LocCtxt); + if (!InitVal.isUndef()) + State = State->bindLoc(FieldLoc, InitVal, LocCtxt); } // Decay the Loc into an RValue, because there might be a Index: clang/test/Analysis/array-init-loop.cpp =================================================================== --- clang/test/Analysis/array-init-loop.cpp +++ clang/test/Analysis/array-init-loop.cpp @@ -223,3 +223,27 @@ clang_analyzer_eval(moved.arr[2].i == 5); // expected-warning{{TRUE}} clang_analyzer_eval(moved.arr[3].i == 6); // expected-warning{{TRUE}} } + +// This snippet used to crash +namespace crash { + +struct S +{ + int x; + S() { x = 1; } +}; + +void no_crash() { + S arr[0]; + int n = 1; + + auto l = [arr, n] { return n; }; + + int x = l(); + clang_analyzer_eval(x == 1); // expected-warning{{TRUE}} + + // FIXME: This should be 'Undefined'. + clang_analyzer_eval(arr[0].x); // expected-warning{{UNKNOWN}} +} + +} Index: clang/test/Analysis/dtor-array.cpp =================================================================== --- clang/test/Analysis/dtor-array.cpp +++ clang/test/Analysis/dtor-array.cpp @@ -204,8 +204,7 @@ auto *arr = new InlineDtor[0]; delete[] arr; - // FIXME: Should be TRUE once the constructors are handled properly. - clang_analyzer_eval(InlineDtor::dtorCalled == 0); // expected-warning {{TRUE}} expected-warning {{FALSE}} + clang_analyzer_eval(InlineDtor::dtorCalled == 0); // expected-warning {{TRUE}} } Index: clang/test/Analysis/zero-size-non-pod-array.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/zero-size-non-pod-array.cpp @@ -0,0 +1,100 @@ +// 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 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}} +} + +#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}} +} + +struct Wrapper{ + S arr[0]; +}; + +void zeroSizeArrayMember() { + S::CtorInvocationCount = 0; + + Wrapper W; + + clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}} +} + +void zeroSizeArrayMemberCopyMove() { + S::CtorInvocationCount = 0; + + Wrapper W; + Wrapper W2 = W; + Wrapper W3 = (Wrapper&&) W2; + + clang_analyzer_eval(S::CtorInvocationCount == 0); //expected-warning{{TRUE}} +}