Index: include/clang/Analysis/Analyses/UninitializedValues.h =================================================================== --- include/clang/Analysis/Analyses/UninitializedValues.h +++ include/clang/Analysis/Analyses/UninitializedValues.h @@ -22,11 +22,12 @@ class AnalysisDeclContext; class CFG; +class CXXConstructorDecl; class DeclContext; class Expr; class VarDecl; -/// A use of a variable, which might be uninitialized. +/// A use of a variable or field, which might be uninitialized. class UninitUse { public: struct Branch { @@ -47,14 +48,18 @@ /// Does this use always see an uninitialized value? bool AlwaysUninit; + /// If the use refers to a field, the use happens in this constructor. + const CXXConstructorDecl *Constructor; + /// This use is always uninitialized if it occurs after any of these branches /// is taken. SmallVector UninitBranches; public: - UninitUse(const Expr *User, bool AlwaysUninit) + UninitUse(const Expr *User, bool AlwaysUninit, + const CXXConstructorDecl *Constructor = 0) : User(User), UninitAfterCall(false), UninitAfterDecl(false), - AlwaysUninit(AlwaysUninit) {} + AlwaysUninit(AlwaysUninit), Constructor(Constructor) {} void addUninitBranch(Branch B) { UninitBranches.push_back(B); @@ -65,6 +70,7 @@ /// Get the expression containing the uninitialized use. const Expr *getUser() const { return User; } + const CXXConstructorDecl *getConstructor() const { return Constructor; } /// The kind of uninitialized use. enum Kind { @@ -103,13 +109,13 @@ virtual ~UninitVariablesHandler(); /// Called when the uninitialized variable is used at the given expression. - virtual void handleUseOfUninitVariable(const VarDecl *vd, + virtual void handleUseOfUninitVariable(const ValueDecl *vd, const UninitUse &use) {} /// Called when the uninitialized variable analysis detects the /// idiom 'int x = x'. All other uses of 'x' within the initializer /// are handled by handleUseOfUninitVariable. - virtual void handleSelfInit(const VarDecl *vd) {} + virtual void handleSelfInit(const ValueDecl *vd) {} }; struct UninitVariablesAnalysisStats { Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -1396,20 +1396,20 @@ " initialization">, InGroup; def warn_uninit_var : Warning< - "variable %0 is uninitialized when %select{used here|captured by block}1">, + "%select{variable|field}0 %1 is uninitialized when %select{used here|captured by block}2">, InGroup, DefaultIgnore; def warn_sometimes_uninit_var : Warning< - "variable %0 is %select{used|captured}1 uninitialized whenever " - "%select{'%3' condition is %select{true|false}4|" - "'%3' loop %select{is entered|exits because its condition is false}4|" - "'%3' loop %select{condition is true|exits because its condition is false}4|" - "switch %3 is taken|" + "%select{variable|field}0 %1 is %select{used|captured}2 uninitialized whenever " + "%select{'%4' condition is %select{true|false}5|" + "'%4' loop %select{is entered|exits because its condition is false}5|" + "'%4' loop %select{condition is true|exits because its condition is false}5|" + "switch %4 is taken|" "its declaration is reached|" - "%3 is called}2">, + "%4 is called}3">, InGroup, DefaultIgnore; def warn_maybe_uninit_var : Warning< - "variable %0 may be uninitialized when " - "%select{used here|captured by block}1">, + "%select{variable|field}0 %1 may be uninitialized when " + "%select{used here|captured by block}2">, InGroup, DefaultIgnore; def note_uninit_var_def : Note<"variable %0 is declared here">; def note_uninit_var_use : Note< Index: lib/Analysis/UninitializedValues.cpp =================================================================== --- lib/Analysis/UninitializedValues.cpp +++ lib/Analysis/UninitializedValues.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/Analyses/PostOrderCFGView.h" #include "clang/Analysis/Analyses/UninitializedValues.h" @@ -42,13 +43,25 @@ return false; } +static bool isTrackedField(const FieldDecl *fd, const DeclContext *dc) { + if (fd->isAnonymousStructOrUnion()) + return false; + const CXXConstructorDecl *cd = dyn_cast(dc); + const RecordDecl *declaringClass = fd->getParent(); + while (declaringClass && declaringClass->isAnonymousStructOrUnion()) + declaringClass = dyn_cast(declaringClass->getDeclContext()); + if (!cd || (cd->getParent() != declaringClass)) + return false; + return true; +} + //------------------------------------------------------------------------====// // DeclToIndex: a mapping from Decls we track to value indices. //====------------------------------------------------------------------------// namespace { class DeclToIndex { - llvm::DenseMap map; + llvm::DenseMap map; public: DeclToIndex() {} @@ -59,7 +72,7 @@ unsigned size() const { return map.size(); } /// Returns the bit vector index for a given declaration. - Optional getValueIndex(const VarDecl *d) const; + Optional getValueIndex(const ValueDecl *d) const; }; } @@ -72,10 +85,23 @@ if (isTrackedVar(vd, &dc)) map[vd] = count++; } + + if (const CXXConstructorDecl *CD = dyn_cast(&dc)) { + const CXXRecordDecl *rd = CD->getParent(); + DeclContext::specific_decl_iterator I(rd->decls_begin()), + E(rd->decls_end()); + for (; I != E; ++I) { + const FieldDecl *fd = isa(*I) + ? cast(*I)->getAnonField() + : dyn_cast(*I); + if (fd && isTrackedField(fd, &dc)) + map[fd] = count++; + } + } } -Optional DeclToIndex::getValueIndex(const VarDecl *d) const { - llvm::DenseMap::const_iterator I = map.find(d); +Optional DeclToIndex::getValueIndex(const ValueDecl *d) const { + llvm::DenseMap::const_iterator I = map.find(d); if (I == map.end()) return None; return I->second; @@ -128,10 +154,10 @@ void resetScratch(); - ValueVector::reference operator[](const VarDecl *vd); + ValueVector::reference operator[](const ValueDecl *vd); Value getValue(const CFGBlock *block, const CFGBlock *dstBlock, - const VarDecl *vd) { + const ValueDecl *vd) { const Optional &idx = declToIndex.getValueIndex(vd); assert(idx.hasValue()); return getValueVector(block)[idx.getValue()]; @@ -192,7 +218,7 @@ scratch.reset(); } -ValueVector::reference CFGBlockValues::operator[](const VarDecl *vd) { +ValueVector::reference CFGBlockValues::operator[](const ValueDecl *vd) { const Optional &idx = declToIndex.getValueIndex(vd); assert(idx.hasValue()); return scratch[idx.getValue()]; @@ -263,14 +289,39 @@ //====------------------------------------------------------------------------// namespace { -class FindVarResult { - const VarDecl *vd; - const DeclRefExpr *dr; +class FindDeclResult { + const ValueDecl *vd; + const Expr *expr; +public: + FindDeclResult(const ValueDecl *vd, const Expr *expr) : vd(vd), expr(expr) {} + + const Expr *getExpr() const { return expr; } + const ValueDecl *getDecl() const { return vd; } +}; + +template +class CXXDefaultInitExprVisitor + : public StmtVisitor > { +private: + Wrapped& wrapped; + typedef StmtVisitor > Base; public: - FindVarResult(const VarDecl *vd, const DeclRefExpr *dr) : vd(vd), dr(dr) {} + CXXDefaultInitExprVisitor(Wrapped& wrapped) : wrapped(wrapped) {} - const DeclRefExpr *getDeclRefExpr() const { return dr; } - const VarDecl *getDecl() const { return vd; } + void Visit(Stmt* S) { + wrapped.Visit(S); + Base::Visit(S); + } + + void VisitStmt(Stmt* S) { + llvm::SmallVector children; + for (Stmt::child_range I = S->children(); I; ++I) { + children.push_back(*I); + } + for (std::size_t i = children.size(); i > 0; --i) { + Visit(children[i - 1]); + } + } }; static const Expr *stripCasts(ASTContext &C, const Expr *Ex) { @@ -287,20 +338,42 @@ return Ex; } -/// If E is an expression comprising a reference to a single variable, find that -/// variable. -static FindVarResult findVar(const Expr *E, const DeclContext *DC) { - if (const DeclRefExpr *DRE = - dyn_cast(stripCasts(DC->getParentASTContext(), E))) +const MemberExpr *findLeftmostMemberExpr(const MemberExpr *me) { + const Expr *Base = me; + const MemberExpr *FieldExpr = me; + while (isa(Base)) { + me = cast(Base); + if (!isa(me->getMemberDecl())) + return 0; + if (FieldDecl *fd = cast(me->getMemberDecl())) + if (!fd->isAnonymousStructOrUnion()) + FieldExpr = me; + Base = me->getBase(); + } + return isa(Base) ? FieldExpr : 0; +} + +/// If E is an expression comprising a reference to a single variable or field, +/// find that variable or field. +static FindDeclResult findDecl(const Expr *E, const DeclContext *DC) { + const Expr *SE = stripCasts(DC->getParentASTContext(), E); + if (const DeclRefExpr *DRE = dyn_cast(SE)) { if (const VarDecl *VD = dyn_cast(DRE->getDecl())) if (isTrackedVar(VD, DC)) - return FindVarResult(VD, DRE); - return FindVarResult(0, 0); + return FindDeclResult(VD, DRE); + } else if (const MemberExpr *ME = dyn_cast(SE)) { + if ((ME = findLeftmostMemberExpr(ME))) + if (const FieldDecl *FD = dyn_cast(ME->getMemberDecl())) + if (isTrackedField(FD, DC)) + return FindDeclResult(FD, ME); + } + return FindDeclResult(0, 0); } -/// \brief Classify each DeclRefExpr as an initialization or a use. Any -/// DeclRefExpr which isn't explicitly classified will be assumed to have -/// escaped the analysis and will be treated as an initialization. +/// \brief Classify each DeclRefExpr or MemberExpr as an initialization or a +/// use. Any DeclRefExpr or MemberExpr which isn't explicitly classified will +/// be assumed to have escaped the analysis and will be treated as an +/// initialization. class ClassifyRefs : public StmtVisitor { public: enum Class { @@ -312,10 +385,11 @@ private: const DeclContext *DC; - llvm::DenseMap Classification; + llvm::DenseMap Classification; - bool isTrackedVar(const VarDecl *VD) const { - return ::isTrackedVar(VD, DC); + bool isTracked(const Decl *D) const { + return (isa(D) && ::isTrackedVar(cast(D), DC)) || + (isa(D) && ::isTrackedField(cast(D), (DC))); } void classify(const Expr *E, Class C); @@ -328,17 +402,23 @@ void VisitBinaryOperator(BinaryOperator *BO); void VisitCallExpr(CallExpr *CE); void VisitCastExpr(CastExpr *CE); + void VisitMemberExpr(MemberExpr *ME); + void VisitCXXConstructExpr(CXXConstructExpr *CE); + void VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE); void operator()(Stmt *S) { Visit(S); } - Class get(const DeclRefExpr *DRE) const { - llvm::DenseMap::const_iterator I - = Classification.find(DRE); + Class get(const Expr *E) const { + llvm::DenseMap::const_iterator I = + Classification.find(E); if (I != Classification.end()) return I->second; - const VarDecl *VD = dyn_cast(DRE->getDecl()); - if (!VD || !isTrackedVar(VD)) + const Decl *D = + isa(E) + ? cast(E)->getDecl() + : isa(E) ? cast(E)->getMemberDecl() : 0; + if (D && !isTracked(D)) return Ignore; return Init; @@ -367,16 +447,38 @@ return; } - FindVarResult Var = findVar(E, DC); - if (const DeclRefExpr *DRE = Var.getDeclRefExpr()) - Classification[DRE] = std::max(Classification[DRE], C); + if (const BinaryConditionalOperator *BCO = + dyn_cast(E)) { + classify(BCO->getCommon(), C); + classify(BCO->getFalseExpr(), C); + return; + } + + if (const BinaryOperator *BO = dyn_cast(E)) { + switch (BO->getOpcode()) { + case BO_PtrMemD: + case BO_PtrMemI: + classify(BO->getLHS(), C); + return; + case BO_Comma: + classify(BO->getLHS(), Ignore); + classify(BO->getRHS(), C); + return; + default: + return; + } + } + + FindDeclResult Var = findDecl(E, DC); + if (const Expr *Ex = Var.getExpr()) + Classification[Ex] = C; } void ClassifyRefs::VisitDeclStmt(DeclStmt *DS) { for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); DI != DE; ++DI) { VarDecl *VD = dyn_cast(*DI); - if (VD && isTrackedVar(VD)) + if (VD && isTracked(VD)) if (const DeclRefExpr *DRE = getSelfInitExpr(VD)) Classification[DRE] = SelfInit; } @@ -390,8 +492,6 @@ // use. if (BO->isCompoundAssignmentOp()) classify(BO->getLHS(), Use); - else if (BO->getOpcode() == BO_Assign) - classify(BO->getLHS(), Ignore); } void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) { @@ -399,31 +499,93 @@ // conversion. if (UO->isIncrementDecrementOp()) classify(UO->getSubExpr(), Use); + else if (UO->getOpcode() == UO_AddrOf) + classify(UO->getSubExpr(), Init); +} + +static bool IsPointerToConst(const QualType &QT) { + return QT->hasPointerRepresentation() && + QT->getPointeeType().isConstQualified(); } void ClassifyRefs::VisitCallExpr(CallExpr *CE) { // If a value is passed by const reference to a function, we should not assume // that it is initialized by the call, and we conservatively do not assume // that it is used. + // If the address of a value is passed to a function, we treat the value as + // initialized. for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); - I != E; ++I) - if ((*I)->getType().isConstQualified() && (*I)->isGLValue()) - classify(*I, Ignore); + I != E; ++I) { + if ((*I)->isGLValue() && (*I)->getType().isConstQualified()) { + classify((*I), Ignore); + continue; + } + + if ((*I)->getType()->hasPointerRepresentation()) { + const Expr *Ex = stripCasts(DC->getParentASTContext(), *I); + const UnaryOperator *UO = dyn_cast(Ex); + if (UO && UO->getOpcode() == UO_AddrOf) + Ex = stripCasts(DC->getParentASTContext(), UO->getSubExpr()); + classify(Ex, (*I)->getType().isConstQualified() ? Ignore : Init); + } + } + if (CXXMemberCallExpr *MCE = dyn_cast(CE)) + if (const MemberExpr *ME = + dyn_cast(MCE->getImplicitObjectArgument())) { + classify(ME, Use); + } } void ClassifyRefs::VisitCastExpr(CastExpr *CE) { - if (CE->getCastKind() == CK_LValueToRValue) - classify(CE->getSubExpr(), Use); - else if (CStyleCastExpr *CSE = dyn_cast(CE)) { + if (CStyleCastExpr *CSE = dyn_cast(CE)) { if (CSE->getType()->isVoidType()) { // Squelch any detected load of an uninitialized value if // we cast it to void. // e.g. (void) x; classify(CSE->getSubExpr(), Ignore); } + return; } + + switch (CE->getCastKind()) { + case CK_LValueToRValue: + classify(CE->getSubExpr(), Use); + break; + case CK_ArrayToPointerDecay: + classify(CE->getSubExpr(), Ignore); + break; + case CK_FunctionToPointerDecay: + if (const MemberExpr *ME = dyn_cast(CE->getSubExpr())) { + if (isa(ME->getMemberDecl()) && + cast(ME->getMemberDecl())->isStatic()) + classify(ME->getBase(), Ignore); + } + break; + default: + break; + } +} + +void ClassifyRefs::VisitMemberExpr(MemberExpr *ME) { + if (ME == findLeftmostMemberExpr(ME) && + ME->getMemberDecl()->getType()->isReferenceType()) { + classify(ME, Use); + } +} + +void ClassifyRefs::VisitCXXConstructExpr(CXXConstructExpr *CE) { + if (CE->getConstructor()->isCopyOrMoveConstructor()) + classify(CE->getArg(0), Use); } +void ClassifyRefs::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { + if (DIE->getExpr() == 0) + return; + CXXDefaultInitExprVisitor V(*this); + V.Visit((Stmt*) DIE->getExpr()); +} + + //------------------------------------------------------------------------====// // Transfer function for uninitialized values analysis. //====------------------------------------------------------------------------// @@ -437,36 +599,48 @@ const ClassifyRefs &classification; ObjCNoReturn objCNoRet; UninitVariablesHandler &handler; + bool reportConstructor; + const CXXConstructorDecl* constructor; + bool isUnionConstructor; public: - TransferFunctions(CFGBlockValues &vals, const CFG &cfg, - const CFGBlock *block, AnalysisDeclContext &ac, - const ClassifyRefs &classification, - UninitVariablesHandler &handler) - : vals(vals), cfg(cfg), block(block), ac(ac), - classification(classification), objCNoRet(ac.getASTContext()), - handler(handler) {} + TransferFunctions(CFGBlockValues &vals, const CFG &cfg, const CFGBlock *block, + AnalysisDeclContext &ac, const ClassifyRefs &classification, + UninitVariablesHandler &handler, + const CXXConstructorDecl *constructor, + bool isUnionConstructor) + : vals(vals), cfg(cfg), block(block), ac(ac), + classification(classification), objCNoRet(ac.getASTContext()), + handler(handler), reportConstructor(false), constructor(constructor), + isUnionConstructor(isUnionConstructor) {} - void reportUse(const Expr *ex, const VarDecl *vd); + void reportUse(const Expr *ex, const ValueDecl *vd); + void setAllFields(Value v); void VisitBinaryOperator(BinaryOperator *bo); void VisitBlockExpr(BlockExpr *be); void VisitCallExpr(CallExpr *ce); void VisitDeclRefExpr(DeclRefExpr *dr); void VisitDeclStmt(DeclStmt *ds); + void VisitCXXMemberCallExpr(CXXMemberCallExpr *mce); + void VisitCXXOperatorCallExpr(CXXOperatorCallExpr *oce); + void VisitMemberExpr(MemberExpr *me); void VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS); void VisitObjCMessageExpr(ObjCMessageExpr *ME); + void VisitCXXDefaultInitExpr(CXXDefaultInitExpr *die); + void VisitCXXConstructExpr(CXXConstructExpr *ce); bool isTrackedVar(const VarDecl *vd) { return ::isTrackedVar(vd, cast(ac.getDecl())); } - FindVarResult findVar(const Expr *ex) { - return ::findVar(ex, cast(ac.getDecl())); + FindDeclResult findDecl(const Expr *ex) { + return ::findDecl(ex, cast(ac.getDecl())); } - UninitUse getUninitUse(const Expr *ex, const VarDecl *vd, Value v) { - UninitUse Use(ex, isAlwaysUninit(v)); + UninitUse getUninitUse(const Expr *ex, const ValueDecl *vd, Value v) { + UninitUse Use(ex, isAlwaysUninit(v), + (reportConstructor && isa(vd)) ? constructor : 0); assert(isUninitialized(v)); if (Use.getKind() == UninitUse::Always) @@ -611,9 +785,31 @@ return Use; } }; + +const CXXThisExpr *findThisExpr(const Expr *E) { + const Expr *OE; + do { + OE = E->IgnoreParenCasts(); + if (const UnaryOperator *UO = dyn_cast(E)) + E = UO->getSubExpr()->IgnoreParenCasts(); + else if (const ArraySubscriptExpr *ASE = dyn_cast(E)) + E = ASE->getLHS()->IgnoreParenCasts(); + } while (OE != E); + return dyn_cast(E); +} +} + +void TransferFunctions::setAllFields(Value v) { + assert(constructor); + const CXXRecordDecl *RD = constructor->getParent(); + for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I) { + if (isTrackedField(*I, constructor)) + vals[*I] = v; + } } -void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) { +void TransferFunctions::reportUse(const Expr *ex, const ValueDecl *vd) { Value v = vals[vd]; if (isUninitialized(v)) handler.handleUseOfUninitVariable(vd, getUninitUse(ex, vd, v)); @@ -643,6 +839,22 @@ } } +void TransferFunctions::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *oce) { + if (!constructor) + return; + + if (oce->getOperator() == OO_Equal) { + const CXXMethodDecl *Callee = cast(oce->getCalleeDecl()); + const Expr *lhs = + stripCasts(constructor->getParentASTContext(), oce->getArgs()[0]); + if (const UnaryOperator *UO = dyn_cast(lhs)) { + if (isa(UO->getSubExpr()) && + constructor->getParent() == Callee->getParent()) + setAllFields(Initialized); + } + } +} + void TransferFunctions::VisitCallExpr(CallExpr *ce) { if (Decl *Callee = ce->getCalleeDecl()) { if (Callee->hasAttr()) { @@ -664,6 +876,35 @@ vals.setAllScratchValues(Unknown); } } + if (const CXXConstructorDecl *CD = + dyn_cast(ac.getDecl())) { + // If "this" is passed to a function without the const qualifier, assume + // that all the fields are initialized. + for (CallExpr::arg_iterator I = ce->arg_begin(), E = ce->arg_end(); I != E; + ++I) { + if (isa(stripCasts(CD->getParentASTContext(), *I)) && + !IsPointerToConst((*I)->getType())) { + setAllFields(Initialized); + break; + } + } + } +} + +void TransferFunctions::VisitCXXMemberCallExpr(CXXMemberCallExpr *mce) { + if (!constructor) + return; + // After a call to a member function within the constructor, it's possible + // that some or all of the fields have been initialized. Just mark them + // conservatively as such. + if (isa(mce->getImplicitObjectArgument())) { + // Make sure it's not an inherited method. + if (constructor->getParent() == + cast(mce->getCalleeDecl())->getParent()) { + setAllFields(Initialized); + return; + } + } } void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) { @@ -677,16 +918,41 @@ vals[cast(dr->getDecl())] = Initialized; break; case ClassifyRefs::SelfInit: - handler.handleSelfInit(cast(dr->getDecl())); + handler.handleSelfInit(cast(dr->getDecl())); break; } } +void TransferFunctions::VisitMemberExpr(MemberExpr *me) { + if (const MemberExpr *e = findLeftmostMemberExpr(me)) { + switch (classification.get(e)) { + case ClassifyRefs::Ignore: + break; + case ClassifyRefs::Use: + case ClassifyRefs::SelfInit: + reportUse(e, cast(e->getMemberDecl())); + break; + case ClassifyRefs::Init: + if (isUnionConstructor) + setAllFields(Initialized); + else + vals[cast(e->getMemberDecl())] = Initialized; + break; + } + } +} + void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) { if (BO->getOpcode() == BO_Assign) { - FindVarResult Var = findVar(BO->getLHS()); - if (const VarDecl *VD = Var.getDecl()) - vals[VD] = Initialized; + FindDeclResult Var = findDecl(BO->getLHS()); + if (const ValueDecl *VD = Var.getDecl()) { + if (!VD->getType()->isReferenceType()) + vals[VD] = Initialized; + } else if (constructor) { + // We treat this as if it escaped analysis. + if (findThisExpr(BO->getLHS())) + setAllFields(Initialized); + } } } @@ -735,6 +1001,19 @@ } } +void TransferFunctions::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *die) { + if (!die->getExpr()) + return; + CXXDefaultInitExprVisitor V(*this); + reportConstructor = true; + V.Visit((Stmt *)die->getExpr()); + reportConstructor = false; +} + +void TransferFunctions::VisitCXXConstructExpr(CXXConstructExpr *ce) { + if (ce->getConstructionKind() == CXXConstructExpr::CK_Delegating) + setAllFields(Initialized); +} //------------------------------------------------------------------------====// // High-level "driver" logic for uninitialized values analysis. //====------------------------------------------------------------------------// @@ -756,12 +1035,26 @@ isFirst = false; } } + // Apply the transfer function. - TransferFunctions tf(vals, cfg, block, ac, classification, handler); + const CXXConstructorDecl *constructor = + dyn_cast(ac.getDecl()); + bool isUnionConstructor = constructor && constructor->getParent()->isUnion(); + TransferFunctions tf(vals, cfg, block, ac, classification, handler, + constructor, isUnionConstructor); for (CFGBlock::const_iterator I = block->begin(), E = block->end(); I != E; ++I) { if (Optional cs = I->getAs()) tf.Visit(const_cast(cs->getStmt())); + else if (Optional ci = I->getAs()) { + FieldDecl* fd = ci->getInitializer()->getMember(); + if (fd && isTrackedField(fd, constructor)) { + if (isUnionConstructor) + tf.setAllFields(Initialized); + else + vals[fd] = Initialized; + } + } } return vals.updateValueVectorWithScratch(block); } @@ -787,7 +1080,7 @@ /// The current block to scribble use information. unsigned currentBlock; - virtual void handleUseOfUninitVariable(const VarDecl *vd, + virtual void handleUseOfUninitVariable(const ValueDecl *vd, const UninitUse &use) { hadUse[currentBlock] = true; hadAnyUse = true; @@ -796,7 +1089,7 @@ /// Called when the uninitialized variable analysis detects the /// idiom 'int x = x'. All other uses of 'x' within the initializer /// are handled by handleUseOfUninitVariable. - virtual void handleSelfInit(const VarDecl *vd) { + virtual void handleSelfInit(const ValueDecl *vd) { hadUse[currentBlock] = true; hadAnyUse = true; } Index: lib/Sema/AnalysisBasedWarnings.cpp =================================================================== --- lib/Sema/AnalysisBasedWarnings.cpp +++ lib/Sema/AnalysisBasedWarnings.cpp @@ -488,25 +488,33 @@ } /// DiagUninitUse -- Helper function to produce a diagnostic for an -/// uninitialized use of a variable. -static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, +/// uninitialized use of a variable or field. +static void DiagUninitUse(Sema &S, const DeclaratorDecl *DD, const UninitUse &Use, bool IsCapturedByBlock) { bool Diagnosed = false; switch (Use.getKind()) { case UninitUse::Always: - S.Diag(Use.getUser()->getLocStart(), diag::warn_uninit_var) - << VD->getDeclName() << IsCapturedByBlock - << Use.getUser()->getSourceRange(); + if (isa(DD) && DD->getType()->isReferenceType()) { + S.Diag(Use.getUser()->getLocStart(), diag::warn_reference_field_is_uninit) + << DD->getDeclName(); + } else { + S.Diag(Use.getUser()->getLocStart(), diag::warn_uninit_var) + << isa(DD) << DD->getDeclName() << IsCapturedByBlock + << Use.getUser()->getSourceRange(); + } + if (const CXXConstructorDecl *Constructor = Use.getConstructor()) + S.Diag(Constructor->getLocation(), diag::note_uninit_in_this_constructor) + << (Constructor->isDefaultConstructor() && Constructor->isImplicit()); return; case UninitUse::AfterDecl: case UninitUse::AfterCall: - S.Diag(VD->getLocation(), diag::warn_sometimes_uninit_var) - << VD->getDeclName() << IsCapturedByBlock + S.Diag(DD->getLocation(), diag::warn_sometimes_uninit_var) + << isa(DD) << DD->getDeclName() << IsCapturedByBlock << (Use.getKind() == UninitUse::AfterDecl ? 4 : 5) - << const_cast(VD->getLexicalDeclContext()) - << VD->getSourceRange(); + << const_cast(DD->getLexicalDeclContext()) + << DD->getSourceRange(); S.Diag(Use.getUser()->getLocStart(), diag::note_uninit_var_use) << IsCapturedByBlock << Use.getUser()->getSourceRange(); return; @@ -639,8 +647,8 @@ } S.Diag(Range.getBegin(), diag::warn_sometimes_uninit_var) - << VD->getDeclName() << IsCapturedByBlock << DiagKind - << Str << I->Output << Range; + << isa(DD) << DD->getDeclName() << IsCapturedByBlock + << DiagKind << Str << I->Output << Range; S.Diag(User->getLocStart(), diag::note_uninit_var_use) << IsCapturedByBlock << User->getSourceRange(); if (RemoveDiagKind != -1) @@ -652,7 +660,7 @@ if (!Diagnosed) S.Diag(Use.getUser()->getLocStart(), diag::warn_maybe_uninit_var) - << VD->getDeclName() << IsCapturedByBlock + << isa(DD) << DD->getDeclName() << IsCapturedByBlock << Use.getUser()->getSourceRange(); } @@ -712,6 +720,13 @@ return true; } +static bool DiagnoseUninitializedUse(Sema &S, const FieldDecl *FD, + const UninitUse &Use, + bool alwaysReportSelfInit = false) { + DiagUninitUse(S, FD, Use, false); + return true; +} + namespace { class FallthroughMapper : public RecursiveASTVisitor { public: @@ -1189,7 +1204,7 @@ // Prefer using MapVector to DenseMap, so that iteration order will be // the same as insertion order. This is needed to obtain a deterministic // order of diagnostics when calling flushDiagnostics(). - typedef llvm::MapVector UsesMap; + typedef llvm::MapVector UsesMap; UsesMap *uses; public: @@ -1198,7 +1213,7 @@ flushDiagnostics(); } - MappedType &getUses(const VarDecl *vd) { + MappedType &getUses(const Decl *vd) { if (!uses) uses = new UsesMap(); @@ -1209,21 +1224,21 @@ return V; } - void handleUseOfUninitVariable(const VarDecl *vd, const UninitUse &use) { + void handleUseOfUninitVariable(const ValueDecl *vd, const UninitUse &use) { getUses(vd).getPointer()->push_back(use); } - - void handleSelfInit(const VarDecl *vd) { + + void handleSelfInit(const ValueDecl *vd) { getUses(vd).setInt(true); } - + void flushDiagnostics() { if (!uses) return; for (UsesMap::iterator i = uses->begin(), e = uses->end(); i != e; ++i) { - const VarDecl *vd = i->first; const MappedType &V = i->second; + const Decl* d = i->first; UsesVec *vec = V.getPointer(); bool hasSelfInit = V.getInt(); @@ -1231,11 +1246,13 @@ // Specially handle the case where we have uses of an uninitialized // variable, but the root cause is an idiomatic self-init. We want // to report the diagnostic at the self-init since that is the root cause. - if (!vec->empty() && hasSelfInit && hasAlwaysUninitializedUse(vec)) - DiagnoseUninitializedUse(S, vd, - UninitUse(vd->getInit()->IgnoreParenCasts(), - /* isAlwaysUninit */ true), - /* alwaysReportSelfInit */ true); + if (!vec->empty() && hasSelfInit && hasAlwaysUninitializedUse(vec) && + isa(d)) + DiagnoseUninitializedUse( + S, cast(d), + UninitUse(cast(d)->getInit()->IgnoreParenCasts(), + /* isAlwaysUninit */ true), + /* alwaysReportSelfInit */ true); else { // Sort the uses by their SourceLocations. While not strictly // guaranteed to produce them in line/column order, this will provide @@ -1247,13 +1264,14 @@ // If we have self-init, downgrade all uses to 'may be uninitialized'. UninitUse Use = hasSelfInit ? UninitUse(vi->getUser(), false) : *vi; - if (DiagnoseUninitializedUse(S, vd, Use)) + if (isa(d) + ? DiagnoseUninitializedUse(S, cast(d), Use) + : DiagnoseUninitializedUse(S, cast(d), Use)) // Skip further diagnostics for this variable. We try to warn only // on the first point at which a variable is used uninitialized. break; } } - // Release the uses vector. delete vec; } @@ -1602,7 +1620,7 @@ // Do not do any analysis for declarations in system headers if we are // going to just ignore them. - if (Diags.getSuppressSystemWarnings() && + if (Diags.getSuppressSystemWarnings() && D->getLocation().isValid() && S.SourceMgr.isInSystemHeader(D->getLocation())) return; @@ -1648,6 +1666,8 @@ .setAlwaysAdd(Stmt::BlockExprClass) .setAlwaysAdd(Stmt::CStyleCastExprClass) .setAlwaysAdd(Stmt::DeclRefExprClass) + .setAlwaysAdd(Stmt::MemberExprClass) + .setAlwaysAdd(Stmt::CXXDefaultInitExprClass) .setAlwaysAdd(Stmt::ImplicitCastExprClass) .setAlwaysAdd(Stmt::UnaryOperatorClass) .setAlwaysAdd(Stmt::AttributedStmtClass); Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -30,6 +30,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Sema/AnalysisBasedWarnings.h" #include "clang/Sema/CXXFieldCollector.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/Initialization.h" @@ -2118,219 +2119,6 @@ return Member; } -namespace { - class UninitializedFieldVisitor - : public EvaluatedExprVisitor { - Sema &S; - // List of Decls to generate a warning on. Also remove Decls that become - // initialized. - llvm::SmallPtrSet &Decls; - // If non-null, add a note to the warning pointing back to the constructor. - const CXXConstructorDecl *Constructor; - public: - typedef EvaluatedExprVisitor Inherited; - UninitializedFieldVisitor(Sema &S, - llvm::SmallPtrSet &Decls, - const CXXConstructorDecl *Constructor) - : Inherited(S.Context), S(S), Decls(Decls), - Constructor(Constructor) { } - - void HandleMemberExpr(MemberExpr *ME, bool CheckReferenceOnly) { - if (isa(ME->getMemberDecl())) - return; - - // FieldME is the inner-most MemberExpr that is not an anonymous struct - // or union. - MemberExpr *FieldME = ME; - - Expr *Base = ME; - while (isa(Base)) { - ME = cast(Base); - - if (isa(ME->getMemberDecl())) - return; - - if (FieldDecl *FD = dyn_cast(ME->getMemberDecl())) - if (!FD->isAnonymousStructOrUnion()) - FieldME = ME; - - Base = ME->getBase(); - } - - if (!isa(Base)) - return; - - ValueDecl* FoundVD = FieldME->getMemberDecl(); - - if (!Decls.count(FoundVD)) - return; - - const bool IsReference = FoundVD->getType()->isReferenceType(); - - // Prevent double warnings on use of unbounded references. - if (IsReference != CheckReferenceOnly) - return; - - unsigned diag = IsReference - ? diag::warn_reference_field_is_uninit - : diag::warn_field_is_uninit; - S.Diag(FieldME->getExprLoc(), diag) << FoundVD; - if (Constructor) - S.Diag(Constructor->getLocation(), - diag::note_uninit_in_this_constructor) - << (Constructor->isDefaultConstructor() && Constructor->isImplicit()); - - } - - void HandleValue(Expr *E) { - E = E->IgnoreParens(); - - if (MemberExpr *ME = dyn_cast(E)) { - HandleMemberExpr(ME, false /*CheckReferenceOnly*/); - return; - } - - if (ConditionalOperator *CO = dyn_cast(E)) { - HandleValue(CO->getTrueExpr()); - HandleValue(CO->getFalseExpr()); - return; - } - - if (BinaryConditionalOperator *BCO = - dyn_cast(E)) { - HandleValue(BCO->getCommon()); - HandleValue(BCO->getFalseExpr()); - return; - } - - if (BinaryOperator *BO = dyn_cast(E)) { - switch (BO->getOpcode()) { - default: - return; - case(BO_PtrMemD): - case(BO_PtrMemI): - HandleValue(BO->getLHS()); - return; - case(BO_Comma): - HandleValue(BO->getRHS()); - return; - } - } - } - - void VisitMemberExpr(MemberExpr *ME) { - // All uses of unbounded reference fields will warn. - HandleMemberExpr(ME, true /*CheckReferenceOnly*/); - - Inherited::VisitMemberExpr(ME); - } - - void VisitImplicitCastExpr(ImplicitCastExpr *E) { - if (E->getCastKind() == CK_LValueToRValue) - HandleValue(E->getSubExpr()); - - Inherited::VisitImplicitCastExpr(E); - } - - void VisitCXXConstructExpr(CXXConstructExpr *E) { - if (E->getConstructor()->isCopyConstructor()) - if (ImplicitCastExpr* ICE = dyn_cast(E->getArg(0))) - if (ICE->getCastKind() == CK_NoOp) - if (MemberExpr *ME = dyn_cast(ICE->getSubExpr())) - HandleMemberExpr(ME, false /*CheckReferenceOnly*/); - - Inherited::VisitCXXConstructExpr(E); - } - - void VisitCXXMemberCallExpr(CXXMemberCallExpr *E) { - Expr *Callee = E->getCallee(); - if (isa(Callee)) - HandleValue(Callee); - - Inherited::VisitCXXMemberCallExpr(E); - } - - void VisitBinaryOperator(BinaryOperator *E) { - // If a field assignment is detected, remove the field from the - // uninitiailized field set. - if (E->getOpcode() == BO_Assign) - if (MemberExpr *ME = dyn_cast(E->getLHS())) - if (FieldDecl *FD = dyn_cast(ME->getMemberDecl())) - if (!FD->getType()->isReferenceType()) - Decls.erase(FD); - - Inherited::VisitBinaryOperator(E); - } - }; - static void CheckInitExprContainsUninitializedFields( - Sema &S, Expr *E, llvm::SmallPtrSet &Decls, - const CXXConstructorDecl *Constructor) { - if (Decls.size() == 0) - return; - - if (!E) - return; - - if (CXXDefaultInitExpr *Default = dyn_cast(E)) { - E = Default->getExpr(); - if (!E) - return; - // In class initializers will point to the constructor. - UninitializedFieldVisitor(S, Decls, Constructor).Visit(E); - } else { - UninitializedFieldVisitor(S, Decls, 0).Visit(E); - } - } - - // Diagnose value-uses of fields to initialize themselves, e.g. - // foo(foo) - // where foo is not also a parameter to the constructor. - // Also diagnose across field uninitialized use such as - // x(y), y(x) - // TODO: implement -Wuninitialized and fold this into that framework. - static void DiagnoseUninitializedFields( - Sema &SemaRef, const CXXConstructorDecl *Constructor) { - - if (SemaRef.getDiagnostics().getDiagnosticLevel(diag::warn_field_is_uninit, - Constructor->getLocation()) - == DiagnosticsEngine::Ignored) { - return; - } - - if (Constructor->isInvalidDecl()) - return; - - const CXXRecordDecl *RD = Constructor->getParent(); - - // Holds fields that are uninitialized. - llvm::SmallPtrSet UninitializedFields; - - // At the beginning, all fields are uninitialized. - for (DeclContext::decl_iterator I = RD->decls_begin(), E = RD->decls_end(); - I != E; ++I) { - if (FieldDecl *FD = dyn_cast(*I)) { - UninitializedFields.insert(FD); - } else if (IndirectFieldDecl *IFD = dyn_cast(*I)) { - UninitializedFields.insert(IFD->getAnonField()); - } - } - - for (CXXConstructorDecl::init_const_iterator FieldInit = - Constructor->init_begin(), - FieldInitEnd = Constructor->init_end(); - FieldInit != FieldInitEnd; ++FieldInit) { - - Expr *InitExpr = (*FieldInit)->getInit(); - - CheckInitExprContainsUninitializedFields( - SemaRef, InitExpr, UninitializedFields, Constructor); - - if (FieldDecl *Field = (*FieldInit)->getAnyMember()) - UninitializedFields.erase(Field); - } - } -} // namespace - /// ActOnCXXInClassMemberInitializer - This is invoked after parsing an /// in-class initializer for a non-static C++ class member, and after /// instantiating an in-class initializer in a class template. Such actions @@ -3885,8 +3673,6 @@ DiagnoseBaseOrMemInitializerOrder(*this, Constructor, MemInits); SetCtorInitializers(Constructor, AnyErrors, MemInits); - - DiagnoseUninitializedFields(*this, Constructor); } void @@ -4016,7 +3802,6 @@ if (CXXConstructorDecl *Constructor = dyn_cast(CDtorDecl)) { SetCtorInitializers(Constructor, /*AnyErrors=*/false); - DiagnoseUninitializedFields(*this, Constructor); } } @@ -8171,7 +7956,8 @@ L->CompletedImplicitDefinition(Constructor); } - DiagnoseUninitializedFields(*this, Constructor); + sema::AnalysisBasedWarnings::Policy P = AnalysisWarnings.getDefaultPolicy(); + AnalysisWarnings.IssueWarnings(P, FunctionScopes.back(), Constructor, 0); } void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) { Index: test/SemaCXX/constructor-initializer.cpp =================================================================== --- test/SemaCXX/constructor-initializer.cpp +++ test/SemaCXX/constructor-initializer.cpp @@ -124,46 +124,6 @@ float *pf; }; -// A silly class used to demonstrate field-is-uninitialized in constructors with -// multiple params. -int IntParam(int i) { return 0; }; -class TwoInOne { public: TwoInOne(TwoInOne a, TwoInOne b) {} }; -class InitializeUsingSelfTest { - bool A; - char* B; - int C; - TwoInOne D; - int E; - InitializeUsingSelfTest(int F) - : A(A), // expected-warning {{field 'A' is uninitialized when used here}} - B((((B)))), // expected-warning {{field 'B' is uninitialized when used here}} - C(A && InitializeUsingSelfTest::C), // expected-warning {{field 'C' is uninitialized when used here}} - D(D, // expected-warning {{field 'D' is uninitialized when used here}} - D), // expected-warning {{field 'D' is uninitialized when used here}} - E(IntParam(E)) {} // expected-warning {{field 'E' is uninitialized when used here}} -}; - -int IntWrapper(int &i) { return 0; }; -class InitializeUsingSelfExceptions { - int A; - int B; - int C; - void *P; - InitializeUsingSelfExceptions(int B) - : A(IntWrapper(A)), // Due to a conservative implementation, we do not report warnings inside function/ctor calls even though it is possible to do so. - B(B), // Not a warning; B is a local variable. - C(sizeof(C)), // sizeof doesn't reference contents, do not warn - P(&P) {} // address-of doesn't reference contents (the pointer may be dereferenced in the same expression but it would be rare; and weird) -}; - -class CopyConstructorTest { - bool A, B, C; - CopyConstructorTest(const CopyConstructorTest& rhs) - : A(rhs.A), - B(B), // expected-warning {{field 'B' is uninitialized when used here}} - C(rhs.C || C) { } // expected-warning {{field 'C' is uninitialized when used here}} -}; - // Make sure we aren't marking default constructors when we shouldn't be. template struct NDC { @@ -285,9 +245,3 @@ int member; // expected-error {{expected ')'}} }; } - -namespace PR14073 { - struct S1 { union { int n; }; S1() : n(n) {} }; // expected-warning {{field 'n' is uninitialized when used here}} - struct S2 { union { union { int n; }; char c; }; S2() : n(n) {} }; // expected-warning {{field 'n' is uninitialized when used here}} - struct S3 { struct { int n; }; S3() : n(n) {} }; // expected-warning {{field 'n' is uninitialized when used here}} -} Index: test/SemaCXX/defaulted-ctor-loop.cpp =================================================================== --- test/SemaCXX/defaulted-ctor-loop.cpp +++ test/SemaCXX/defaulted-ctor-loop.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wuninitialized %s // WARNING: This test may recurse infinitely if failing. Index: test/SemaCXX/uninitialized.cpp =================================================================== --- test/SemaCXX/uninitialized.cpp +++ test/SemaCXX/uninitialized.cpp @@ -122,7 +122,7 @@ A *a22 = new A(a22->count); // expected-warning {{variable 'a22' is uninitialized when used within its own initialization}} A *a23 = new A(a23->ONE); // expected-warning {{variable 'a23' is uninitialized when used within its own initialization}} A *a24 = new A(a24->TWO); // expected-warning {{variable 'a24' is uninitialized when used within its own initialization}} - A *a25 = new A(a25->zero()); // expected-warning {{variable 'a25' is uninitialized when used within its own initialization}} + A *a25 = new A(a25->zero()); // no-warning: zero is a static method. A *a26 = new A(a26->get()); // expected-warning {{variable 'a26' is uninitialized when used within its own initialization}} A *a27 = new A(a27->get2()); // expected-warning {{variable 'a27' is uninitialized when used within its own initialization}} @@ -242,7 +242,7 @@ S(bool (*)[1]) : x(x) {} // expected-warning {{field 'x' is uninitialized when used here}} S(bool (*)[2]) : x(x + 1) {} // expected-warning {{field 'x' is uninitialized when used here}} - S(bool (*)[3]) : x(x + x) {} // expected-warning 2{{field 'x' is uninitialized when used here}} + S(bool (*)[3]) : x(x + x) {} // expected-warning {{field 'x' is uninitialized when used here}} S(bool (*)[4]) : x(static_cast(x) + 1) {} // expected-warning {{field 'x' is uninitialized when used here}} S(bool (*)[5]) : x(foo(x)) {} // expected-warning {{field 'x' is uninitialized when used here}} @@ -345,18 +345,18 @@ int a; // This field needs to be last to prevent the cross field // uninitialized warning. E(char (*)[1]) : a(a ? b : c) {} // expected-warning {{field 'a' is uninitialized when used here}} - E(char (*)[2]) : a(b ? a : a) {} // expected-warning 2{{field 'a' is uninitialized when used here}} + E(char (*)[2]) : a(b ? a : a) {} // expected-warning {{field 'a' is uninitialized when used here}} E(char (*)[3]) : a(b ? (a) : c) {} // expected-warning {{field 'a' is uninitialized when used here}} E(char (*)[4]) : a(b ? c : (a+c)) {} // expected-warning {{field 'a' is uninitialized when used here}} E(char (*)[5]) : a(b ? c : b) {} - E(char (*)[6]) : a(a ?: a) {} // expected-warning 2{{field 'a' is uninitialized when used here}} + E(char (*)[6]) : a(a ?: a) {} // expected-warning {{field 'a' is uninitialized when used here}} E(char (*)[7]) : a(b ?: a) {} // expected-warning {{field 'a' is uninitialized when used here}} E(char (*)[8]) : a(a ?: c) {} // expected-warning {{field 'a' is uninitialized when used here}} E(char (*)[9]) : a(b ?: c) {} E(char (*)[10]) : a((a, a, b)) {} - E(char (*)[11]) : a((c + a, a + 1, b)) {} // expected-warning 2{{field 'a' is uninitialized when used here}} + E(char (*)[11]) : a((c + a, a + 1, b)) {} // expected-warning {{field 'a' is uninitialized when used here}} E(char (*)[12]) : a((b + c, c, a)) {} // expected-warning {{field 'a' is uninitialized when used here}} E(char (*)[13]) : a((a, a, a, a)) {} // expected-warning {{field 'a' is uninitialized when used here}} E(char (*)[14]) : a((b, c, c)) {} @@ -385,6 +385,70 @@ G(char (*)[7]) : f3(f3->*f_ptr) {} // expected-warning {{field 'f3' is uninitialized when used here}} G(char (*)[8]) : f3(new F(f3->*ptr)) {} // expected-warning {{field 'f3' is uninitialized when used here}} }; + + int H2() { return 2; }; + struct H; + void H3(const H *h) {} + void H4(H *h) {} + void H5(int *a) { + *a = 42; + } + void H6(const int &a) { } + + struct H { + int a, b; + F* f; + void H1() {} + H() : a(0), b(1), f(nullptr) {} + H(char (*)[1]) : a(1) { b = a; } + H(char (*)[2]) { b = a; } // expected-warning {{field 'a' is uninitialized when used here}} + H(char (*)[3]) { H1(); b = a; } // no-warning: H1 may initialize a and b + H(char (*)[4]) { H2(); a = b; } // expected-warning {{field 'b' is uninitialized when used here}} + H(char (*)[5]) : b(2) { + a = f->a; // expected-warning {{field 'f' is uninitialized when used here}} + f->a = b; // no-warning: b is initialized + } + H(char (*)[6]) : f(new F()) { a = f->a; f->a = b; } // expected-warning {{field 'b' is uninitialized when used here}} + H(char (*)[7]) : a(2) { + int a1 = a; + int b1 = b; // expected-warning {{field 'b' is uninitialized when used here}} + a = a1 + b1; + F f1 = *f; // expected-warning {{field 'f' is uninitialized when used here}} + b = f1.a; + } + H(char (*)[8]) { H h2; h2.H1(); b = a; } // expected-warning {{field 'a' is uninitialized when used here}} + H(char (*)[9]) { H3(this); b = f->a; } // expected-warning {{field 'f' is uninitialized when used here}} + H(char (*)[10]) { H4(this); b = f->a; } + H(char (*)[11]) { H h2; h2.H1(); b = h2.b; h2.a = a; } // expected-warning {{field 'a' is uninitialized when used here}} + H(char (*)[12]) { + H5(&b); + a = b; + } + H(char (*)[13]) { + H6(b); + a = b; // expected-warning {{field 'b' is uninitialized when used here}} + } + H(const H &h) { a = h.a; b = h.b; f = h.f; } + }; + + struct I { + int c; + G *g; + I(char (*)[1]) { c = g->f2.f->a; } // expected-warning {{field 'g' is uninitialized when used here}} + }; + + union J { + int a; + float b; + J() : a(1) { + float f = b; + b = -f; + } + J(char (*)[1]) { + float x = b; // expected-warning {{field 'b' is uninitialized when used here}} + b = -x; + } + }; } namespace statics { @@ -556,15 +620,15 @@ }; struct C { C() {} // expected-note4{{in this constructor}} - A a1 = a1; // expected-warning {{uninitialized}} - A a2 = a2.get(); // expected-warning {{uninitialized}} + A a1 = a1; // expected-warning{{uninitialized}} + A a2 = a2.get(); // expected-warning{{uninitialized}} A a3 = a3.num(); - A a4 = a4.copy(a4); // expected-warning {{uninitialized}} + A a4 = a4.copy(a4); // expected-warning{{uninitialized}} A a5 = a5.something(a5); A a6 = ref(a6); A a7 = const_ref(a7); A a8 = pointer(&a8); - A a9 = normal(a9); // expected-warning {{uninitialized}} + A a9 = normal(a9); // expected-warning{{uninitialized}} }; struct D { // expected-note4{{in the implicit default constructor}} A a1 = a1; // expected-warning {{uninitialized}} @@ -699,6 +763,9 @@ int &c; Q() : a(c = 5), // expected-warning{{reference 'c' is not yet bound to a value when used here}} + b(c), // no warning: warned above + c(a) {} + Q(int) : b(c), // expected-warning{{reference 'c' is not yet bound to a value when used here}} c(a) {} }; @@ -728,3 +795,48 @@ C() : A(y = 4), x(y) {} }; } + +// A silly class used to demonstrate field-is-uninitialized in constructors with +// multiple params. +int IntParam(int i) { return 0; }; +class TwoInOne { public: TwoInOne(TwoInOne a, TwoInOne b) {} }; +class InitializeUsingSelfTest { + bool A; + char* B; + int C; + TwoInOne D; + int E; + InitializeUsingSelfTest(int F) + : A(A), // expected-warning {{field 'A' is uninitialized when used here}} + B((((B)))), // expected-warning {{field 'B' is uninitialized when used here}} + C(A && InitializeUsingSelfTest::C), // expected-warning {{field 'C' is uninitialized when used here}} + D(D, D), // expected-warning {{field 'D' is uninitialized when used here}} + E(IntParam(E)) {} // expected-warning {{field 'E' is uninitialized when used here}} +}; + +int IntWrapper(int &i) { return 0; }; +class InitializeUsingSelfExceptions { + int A; + int B; + int C; + void *P; + InitializeUsingSelfExceptions(int B) + : A(IntWrapper(A)), // Due to a conservative implementation, we do not report warnings inside function/ctor calls even though it is possible to do so. + B(B), // Not a warning; B is a local variable. + C(sizeof(C)), // sizeof doesn't reference contents, do not warn + P(&P) { (void) this->B; } // address-of doesn't reference contents (the pointer may be dereferenced in the same expression but it would be rare; and weird) +}; + +class CopyConstructorTest { + bool A, B, C; + CopyConstructorTest(const CopyConstructorTest& rhs) + : A(rhs.A), + B(B), // expected-warning {{field 'B' is uninitialized when used here}} + C(rhs.C || C) { } // expected-warning {{field 'C' is uninitialized when used here}} +}; + +namespace PR14073 { + struct S1 { union { int n; }; S1() : n(n) {} }; // expected-warning {{field 'n' is uninitialized when used here}} + struct S2 { union { union { int n; }; char c; }; S2() : n(n) {} }; // expected-warning {{field 'n' is uninitialized when used here}} + struct S3 { struct { int n; }; S3() : n(n) {} }; // expected-warning {{field 'n' is uninitialized when used here}} +}