[analyzer] Handling non-POD multidimensional arrays in ArrayInitLoopExpr
This patch makes it possible for lambdas, implicit copy/move ctors and structured bindings to handle non-POD multidimensional arrays. Differential Revision: https://reviews.llvm.org/D131840
This commit is contained in:
parent
b21de9b38f
commit
c81bf940c7
|
@ -2731,6 +2731,10 @@ public:
|
|||
/// Return number of constant array elements.
|
||||
uint64_t getConstantArrayElementCount(const ConstantArrayType *CA) const;
|
||||
|
||||
/// Return number of elements initialized in an ArrayInitLoopExpr.
|
||||
uint64_t
|
||||
getArrayInitLoopExprElementCount(const ArrayInitLoopExpr *AILE) const;
|
||||
|
||||
/// Perform adjustment on the parameter type of a function.
|
||||
///
|
||||
/// This routine adjusts the given parameter type @p T to the actual
|
||||
|
|
|
@ -1466,6 +1466,8 @@ private:
|
|||
llvm::DenseMap<const DeclStmt *, const DeclStmt *> SyntheticDeclStmts;
|
||||
};
|
||||
|
||||
Expr *extractElementInitializerFromNestedAILE(const ArrayInitLoopExpr *AILE);
|
||||
|
||||
} // namespace clang
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -6905,6 +6905,21 @@ ASTContext::getConstantArrayElementCount(const ConstantArrayType *CA) const {
|
|||
return ElementCount;
|
||||
}
|
||||
|
||||
uint64_t ASTContext::getArrayInitLoopExprElementCount(
|
||||
const ArrayInitLoopExpr *AILE) const {
|
||||
if (!AILE)
|
||||
return 0;
|
||||
|
||||
uint64_t ElementCount = 1;
|
||||
|
||||
do {
|
||||
ElementCount *= AILE->getArraySize().getZExtValue();
|
||||
AILE = dyn_cast<ArrayInitLoopExpr>(AILE->getSubExpr());
|
||||
} while (AILE);
|
||||
|
||||
return ElementCount;
|
||||
}
|
||||
|
||||
/// getFloatingRank - Return a relative rank for floating point types.
|
||||
/// This routine will assert if passed a built-in type that isn't a float.
|
||||
static FloatingRank getFloatingRank(QualType T) {
|
||||
|
|
|
@ -1332,6 +1332,18 @@ private:
|
|||
|
||||
} // namespace
|
||||
|
||||
Expr *
|
||||
clang::extractElementInitializerFromNestedAILE(const ArrayInitLoopExpr *AILE) {
|
||||
if (!AILE)
|
||||
return nullptr;
|
||||
|
||||
Expr *AILEInit = AILE->getSubExpr();
|
||||
while (const auto *E = dyn_cast<ArrayInitLoopExpr>(AILEInit))
|
||||
AILEInit = E->getSubExpr();
|
||||
|
||||
return AILEInit;
|
||||
}
|
||||
|
||||
inline bool AddStmtChoice::alwaysAdd(CFGBuilder &builder,
|
||||
const Stmt *stmt) const {
|
||||
return builder.alwaysAdd(stmt) || kind == AlwaysAdd;
|
||||
|
@ -1706,11 +1718,12 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
|
|||
if (Init) {
|
||||
// If the initializer is an ArrayInitLoopExpr, we want to extract the
|
||||
// initializer, that's used for each element.
|
||||
const auto *AILE = dyn_cast_or_null<ArrayInitLoopExpr>(Init);
|
||||
auto *AILEInit = extractElementInitializerFromNestedAILE(
|
||||
dyn_cast<ArrayInitLoopExpr>(Init));
|
||||
|
||||
findConstructionContexts(
|
||||
ConstructionContextLayer::create(cfg->getBumpVectorContext(), I),
|
||||
AILE ? AILE->getSubExpr() : Init);
|
||||
AILEInit ? AILEInit : Init);
|
||||
|
||||
if (HasTemporaries) {
|
||||
// For expression with temporaries go directly to subexpression to omit
|
||||
|
@ -3415,11 +3428,12 @@ CFGBlock *CFGBuilder::VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc) {
|
|||
if (Expr *Init = *it) {
|
||||
// If the initializer is an ArrayInitLoopExpr, we want to extract the
|
||||
// initializer, that's used for each element.
|
||||
const auto *AILE = dyn_cast_or_null<ArrayInitLoopExpr>(Init);
|
||||
auto *AILEInit = extractElementInitializerFromNestedAILE(
|
||||
dyn_cast<ArrayInitLoopExpr>(Init));
|
||||
|
||||
findConstructionContexts(ConstructionContextLayer::create(
|
||||
cfg->getBumpVectorContext(), {E, Idx}),
|
||||
AILE ? AILE->getSubExpr() : Init);
|
||||
AILEInit ? AILEInit : Init);
|
||||
|
||||
CFGBlock *Tmp = Visit(Init);
|
||||
if (Tmp)
|
||||
|
|
|
@ -537,9 +537,10 @@ ExprEngine::addObjectUnderConstruction(ProgramStateRef State,
|
|||
Init = Item.getCXXCtorInitializer()->getInit();
|
||||
|
||||
// In an ArrayInitLoopExpr the real initializer is returned by
|
||||
// getSubExpr().
|
||||
// getSubExpr(). Note that AILEs can be nested in case of
|
||||
// multidimesnional arrays.
|
||||
if (const auto *AILE = dyn_cast_or_null<ArrayInitLoopExpr>(Init))
|
||||
Init = AILE->getSubExpr();
|
||||
Init = extractElementInitializerFromNestedAILE(AILE);
|
||||
|
||||
// FIXME: Currently the state might already contain the marker due to
|
||||
// incorrect handling of temporaries bound to default parameters.
|
||||
|
|
|
@ -524,16 +524,32 @@ bindRequiredArrayElementToEnvironment(ProgramStateRef State,
|
|||
// | `-DeclRefExpr
|
||||
// `-ArrayInitIndexExpr
|
||||
//
|
||||
// The resulting expression for a multidimensional array.
|
||||
// ArrayInitLoopExpr <-- we're here
|
||||
// |-OpaqueValueExpr
|
||||
// | `-DeclRefExpr <-- match this
|
||||
// `-ArrayInitLoopExpr
|
||||
// |-OpaqueValueExpr
|
||||
// | `-ArraySubscriptExpr
|
||||
// | |-ImplicitCastExpr
|
||||
// | | `-OpaqueValueExpr
|
||||
// | | `-DeclRefExpr
|
||||
// | `-ArrayInitIndexExpr
|
||||
// `-CXXConstructExpr <-- extract this
|
||||
// ` ...
|
||||
|
||||
const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr();
|
||||
|
||||
// HACK: There is no way we can put the index of the array element into the
|
||||
// CFG unless we unroll the loop, so we manually select and bind the required
|
||||
// parameter to the environment.
|
||||
const auto *CE = cast<CXXConstructExpr>(AILE->getSubExpr());
|
||||
const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr();
|
||||
const auto *CE =
|
||||
cast<CXXConstructExpr>(extractElementInitializerFromNestedAILE(AILE));
|
||||
|
||||
SVal Base = UnknownVal();
|
||||
if (const auto *ME = dyn_cast<MemberExpr>(OVESrc))
|
||||
Base = State->getSVal(ME, LCtx);
|
||||
else if (const auto *DRE = cast<DeclRefExpr>(OVESrc))
|
||||
else if (const auto *DRE = dyn_cast<DeclRefExpr>(OVESrc))
|
||||
Base = State->getLValue(cast<VarDecl>(DRE->getDecl()), LCtx);
|
||||
else
|
||||
llvm_unreachable("ArrayInitLoopExpr contains unexpected source expression");
|
||||
|
@ -596,8 +612,9 @@ void ExprEngine::handleConstructor(const Expr *E,
|
|||
if (AILE) {
|
||||
// Only set this once even though we loop through it multiple times.
|
||||
if (!getPendingInitLoop(State, CE, LCtx))
|
||||
State = setPendingInitLoop(State, CE, LCtx,
|
||||
AILE->getArraySize().getLimitedValue());
|
||||
State = setPendingInitLoop(
|
||||
State, CE, LCtx,
|
||||
getContext().getArrayInitLoopExprElementCount(AILE));
|
||||
|
||||
State = bindRequiredArrayElementToEnvironment(
|
||||
State, AILE, LCtx, svalBuilder.makeArrayIndex(Idx));
|
||||
|
|
|
@ -223,3 +223,86 @@ void move_ctor_init_non_pod() {
|
|||
clang_analyzer_eval(moved.arr[2].i == 5); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(moved.arr[3].i == 6); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
//Note: This is the only solution I could find to check the values without
|
||||
// crashing clang. For more details on the crash see Issue #57135.
|
||||
void lambda_capture_multi_array() {
|
||||
S3 arr[2][2] = {1,2,3,4};
|
||||
|
||||
{
|
||||
int x = [arr] { return arr[0][0].i; }();
|
||||
clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
{
|
||||
int x = [arr] { return arr[0][1].i; }();
|
||||
clang_analyzer_eval(x == 2); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
{
|
||||
int x = [arr] { return arr[1][0].i; }();
|
||||
clang_analyzer_eval(x == 3); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
{
|
||||
int x = [arr] { return arr[1][1].i; }();
|
||||
clang_analyzer_eval(x == 4); // expected-warning{{TRUE}}
|
||||
}
|
||||
}
|
||||
|
||||
// This struct will force constructor inlining in MultiWrapper.
|
||||
struct UserDefinedCtor {
|
||||
int i;
|
||||
UserDefinedCtor() {}
|
||||
UserDefinedCtor(const UserDefinedCtor ©) {
|
||||
int j = 1;
|
||||
i = copy.i;
|
||||
}
|
||||
};
|
||||
|
||||
struct MultiWrapper {
|
||||
UserDefinedCtor arr[2][2];
|
||||
};
|
||||
|
||||
void copy_ctor_multi() {
|
||||
MultiWrapper MW;
|
||||
|
||||
MW.arr[0][0].i = 0;
|
||||
MW.arr[0][1].i = 1;
|
||||
MW.arr[1][0].i = 2;
|
||||
MW.arr[1][1].i = 3;
|
||||
|
||||
MultiWrapper MWCopy = MW;
|
||||
|
||||
clang_analyzer_eval(MWCopy.arr[0][0].i == 0); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(MWCopy.arr[0][1].i == 1); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(MWCopy.arr[1][0].i == 2); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(MWCopy.arr[1][1].i == 3); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
void move_ctor_multi() {
|
||||
MultiWrapper MW;
|
||||
|
||||
MW.arr[0][0].i = 0;
|
||||
MW.arr[0][1].i = 1;
|
||||
MW.arr[1][0].i = 2;
|
||||
MW.arr[1][1].i = 3;
|
||||
|
||||
MultiWrapper MWMove = (MultiWrapper &&) MW;
|
||||
|
||||
clang_analyzer_eval(MWMove.arr[0][0].i == 0); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(MWMove.arr[0][1].i == 1); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(MWMove.arr[1][0].i == 2); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(MWMove.arr[1][1].i == 3); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
void structured_binding_multi() {
|
||||
S3 arr[2][2] = {1,2,3,4};
|
||||
|
||||
auto [a,b] = arr;
|
||||
|
||||
clang_analyzer_eval(a[0].i == 1); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(a[1].i == 2); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(b[0].i == 3); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(b[1].i == 4); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue