Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -34,6 +34,119 @@ Bldr.generateNode(ME, Pred, state); } +bool isCopyOrMoveLike(const CXXConstructorDecl *Constr) { + if (Constr->isCopyOrMoveConstructor()) + return true; + + if(Constr->getNumParams() != 1) + return false; + + const auto ParamType = + Constr->getParamDecl(0)->getType()->getUnqualifiedDesugaredType(); + if(!ParamType->isReferenceType()) + return false; + + const auto ParamPointeeType = + ParamType->getAs()->getPointeeType(); + if(!ParamPointeeType->isRecordType()) + return false; + + const auto *ParamRecDecl = ParamPointeeType->getAs()->getDecl(); + const auto *ThisRecDecl = Constr->getParent(); + + if(ParamRecDecl!=ThisRecDecl) + return false; + + return true; +} + +bool isAlmostTrivial(const CXXMethodDecl *Met) { + if (Met->isTrivial()) + return true; + + if(!Met->hasTrivialBody()) + return false; + + if(Met->getNumParams() != 1) + return false; + + const auto *Param = Met->getParamDecl(0); + const auto *ThisRecDecl = Met->getParent(); + + const auto *Constr = dyn_cast(Met); + if(!Constr) + return false; + + if(ThisRecDecl->getNumVBases()>0) + return false; + + for(const auto Base: ThisRecDecl->bases()) { + if(Base.getType()->getAsCXXRecordDecl()->field_empty()) + continue; + for(const auto *Initzer: Constr->inits()) { + if(Initzer->isBaseInitializer() && + Initzer->getBaseClass() == &*Base.getType()) { + if(const auto *CtrCall = dyn_cast(Initzer->getInit()->IgnoreParenImpCasts())) { + if(!isCopyOrMoveLike(CtrCall->getConstructor()) || + !isAlmostTrivial(CtrCall->getConstructor())) + return false; + if(const auto *Init = dyn_cast(CtrCall->getArg(0)->IgnoreParenImpCasts())) { + if(Init->getDecl() != Param) + return false; + } else { + return false; + } + } else { + return false; + } + break; + } + } + } + + for(const auto *Field: ThisRecDecl->fields()) { + if(!Field->getType()->isScalarType() && + !Field->getType()->isRecordType()) + return false; + bool found = false; + for(const auto *Initzer: Constr->inits()) { + if(Initzer->isMemberInitializer() && Initzer->getMember() == Field) { + found = true; + const MemberExpr *InitMem; + if(Field->getType()->isScalarType()) { + InitMem = dyn_cast(Initzer->getInit()->IgnoreParenImpCasts()); + } else { + if(const auto *CtrCall = dyn_cast(Initzer->getInit()->IgnoreParenImpCasts())) { + if(!isCopyOrMoveLike(CtrCall->getConstructor()) || + !isAlmostTrivial(CtrCall->getConstructor())) + return false; + InitMem = dyn_cast(CtrCall->getArg(0)->IgnoreParenImpCasts()); + } else { + return false; + } + } + if(InitMem) { + if(InitMem->getMemberDecl() != Field) + return false; + if(const auto *Base = dyn_cast(InitMem->getBase())) { + if(Base->getDecl() != Param) + return false; + } else { + return false; + } + } else { + return false; + } + break; + } + } + if(!found) + return false; + } + + return true; +} + // FIXME: This is the sort of code that should eventually live in a Core // checker rather than as a special case in ExprEngine. void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, @@ -41,12 +154,12 @@ SVal ThisVal; bool AlwaysReturnsLValue; if (const CXXConstructorCall *Ctor = dyn_cast(&Call)) { - assert(Ctor->getDecl()->isTrivial()); - assert(Ctor->getDecl()->isCopyOrMoveConstructor()); + assert(isAlmostTrivial(Ctor->getDecl())); + assert(isCopyOrMoveLike(Ctor->getDecl())); ThisVal = Ctor->getCXXThisVal(); AlwaysReturnsLValue = false; } else { - assert(cast(Call.getDecl())->isTrivial()); + assert(isAlmostTrivial(cast(Call.getDecl()))); assert(cast(Call.getDecl())->getOverloadedOperator() == OO_Equal); ThisVal = cast(Call).getCXXThisVal(); @@ -332,8 +445,8 @@ StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); bool IsArray = isa(Target); - if (CE->getConstructor()->isTrivial() && - CE->getConstructor()->isCopyOrMoveConstructor() && + if (isAlmostTrivial(CE->getConstructor()) && + isCopyOrMoveLike(CE->getConstructor()) && !IsArray) { // FIXME: Handle other kinds of trivial constructors as well. for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); Index: test/Analysis/ctor.mm =================================================================== --- test/Analysis/ctor.mm +++ test/Analysis/ctor.mm @@ -144,11 +144,11 @@ NonPOD() {} NonPOD(const NonPOD &Other) - : x(Other.x), y(Other.y) // expected-warning {{undefined}} + : x(Other.x), y(Other.y) // no-warning { } NonPOD(NonPOD &&Other) - : x(Other.x), y(Other.y) // expected-warning {{undefined}} + : x(Other.x), y(Other.y) // no-warning { } @@ -174,11 +174,11 @@ Inner() {} Inner(const Inner &Other) - : x(Other.x), y(Other.y) // expected-warning {{undefined}} + : x(Other.x), y(Other.y) // no-warning { } Inner(Inner &&Other) - : x(Other.x), y(Other.y) // expected-warning {{undefined}} + : x(Other.x), y(Other.y) // no-warning { } @@ -224,25 +224,29 @@ void testNonPOD() { NonPOD p; p.x = 1; - NonPOD p2 = p; + NonPOD p2 = p; // no-warning + clang_analyzer_eval(p2.x == 1); // expected-warning{{TRUE}} } void testNonPODMove() { NonPOD p; p.x = 1; - NonPOD p2 = move(p); + NonPOD p2 = move(p); // no-warning + clang_analyzer_eval(p2.x == 1); // expected-warning{{TRUE}} } void testNonPODWrapper() { NonPODWrapper w; w.p.y = 1; - NonPODWrapper w2 = w; + NonPODWrapper w2 = w; // no-warning + clang_analyzer_eval(w2.p.y == 1); // expected-warning{{TRUE}} } void testNonPODWrapperMove() { NonPODWrapper w; w.p.y = 1; - NonPODWrapper w2 = move(w); + NonPODWrapper w2 = move(w); // no-warning + clang_analyzer_eval(w2.p.y == 1); // expected-warning{{TRUE}} } // Not strictly about constructors, but trivial assignment operators should