diff options
author | Manuel Klimek <klimek@google.com> | 2015-03-03 14:54:25 +0000 |
---|---|---|
committer | Manuel Klimek <klimek@google.com> | 2015-03-03 14:54:25 +0000 |
commit | 27ee25f73856ffa904d87f0fe1b49ae934e6b4bb (patch) | |
tree | 2b4016b96e7e0d46945cb0adeb271c1d65adfaf8 /clang | |
parent | 2d293400950ac0c72c18d31e54f70716830b6e67 (diff) | |
download | bcm5719-llvm-27ee25f73856ffa904d87f0fe1b49ae934e6b4bb.tar.gz bcm5719-llvm-27ee25f73856ffa904d87f0fe1b49ae934e6b4bb.zip |
Make -Wuninitialized warn on pointer-to-member and comma operators.
`isTrackedVar` has been updated to also track records.
`DeclRefExpr`s appearing on the left side of a comma operator are
ignored, while those appearing on the right side are classified as
`Use`.
Patch by Enrico Pertoso.
llvm-svn: 231068
Diffstat (limited to 'clang')
-rw-r--r-- | clang/lib/Analysis/UninitializedValues.cpp | 55 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 7 | ||||
-rw-r--r-- | clang/test/SemaCXX/uninitialized.cpp | 45 |
3 files changed, 94 insertions, 13 deletions
diff --git a/clang/lib/Analysis/UninitializedValues.cpp b/clang/lib/Analysis/UninitializedValues.cpp index 61a259217d7..8a6870ffa4d 100644 --- a/clang/lib/Analysis/UninitializedValues.cpp +++ b/clang/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" @@ -37,7 +38,7 @@ static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc) { !vd->isExceptionVariable() && !vd->isInitCapture() && vd->getDeclContext() == dc) { QualType ty = vd->getType(); - return ty->isScalarType() || ty->isVectorType(); + return ty->isScalarType() || ty->isVectorType() || ty->isRecordType(); } return false; } @@ -347,6 +348,7 @@ public: } static const DeclRefExpr *getSelfInitExpr(VarDecl *VD) { + if (VD->getType()->isRecordType()) return nullptr; if (Expr *Init = VD->getInit()) { const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(stripCasts(VD->getASTContext(), Init)); @@ -376,10 +378,26 @@ void ClassifyRefs::classify(const Expr *E, Class C) { return; } + if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) { + if (VarDecl *VD = dyn_cast<VarDecl>(ME->getMemberDecl())) { + if (!VD->isStaticDataMember()) + classify(ME->getBase(), C); + } + return; + } + if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) { - if (BO->getOpcode() == BO_Comma) + switch (BO->getOpcode()) { + case BO_PtrMemD: + case BO_PtrMemI: + classify(BO->getLHS(), C); + return; + case BO_Comma: classify(BO->getRHS(), C); - return; + return; + default: + return; + } } FindVarResult Var = findVar(E, DC); @@ -404,7 +422,7 @@ void ClassifyRefs::VisitBinaryOperator(BinaryOperator *BO) { // use. if (BO->isCompoundAssignmentOp()) classify(BO->getLHS(), Use); - else if (BO->getOpcode() == BO_Assign) + else if (BO->getOpcode() == BO_Assign || BO->getOpcode() == BO_Comma) classify(BO->getLHS(), Ignore); } @@ -415,25 +433,40 @@ void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) { classify(UO->getSubExpr(), Use); } +static bool isPointerToConst(const QualType &QT) { + return QT->isAnyPointerType() && QT->getPointeeType().isConstQualified(); +} + void ClassifyRefs::VisitCallExpr(CallExpr *CE) { // Classify arguments to std::move as used. if (CE->getNumArgs() == 1) { if (FunctionDecl *FD = CE->getDirectCallee()) { if (FD->isInStdNamespace() && FD->getIdentifier() && FD->getIdentifier()->isStr("move")) { - classify(CE->getArg(0), Use); + // RecordTypes are handled in SemaDeclCXX.cpp. + if (!CE->getArg(0)->getType()->isRecordType()) + classify(CE->getArg(0), Use); return; } } } - // 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 a value is passed by const pointer or 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. 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()) { + if ((*I)->getType().isConstQualified()) + classify((*I), Ignore); + } else if (isPointerToConst((*I)->getType())) { + const Expr *Ex = stripCasts(DC->getParentASTContext(), *I); + const UnaryOperator *UO = dyn_cast<UnaryOperator>(Ex); + if (UO && UO->getOpcode() == UO_AddrOf) + Ex = UO->getSubExpr(); + classify(Ex, Ignore); + } + } } void ClassifyRefs::VisitCastExpr(CastExpr *CE) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index eb5b7154936..b1268cc2336 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -8582,8 +8582,13 @@ namespace { diag = diag::warn_uninit_self_reference_in_reference_init; } else if (cast<VarDecl>(OrigDecl)->isStaticLocal()) { diag = diag::warn_static_self_reference_in_init; - } else { + } else if (isa<TranslationUnitDecl>(OrigDecl->getDeclContext()) || + isa<NamespaceDecl>(OrigDecl->getDeclContext()) || + DRE->getDecl()->getType()->isRecordType()) { diag = diag::warn_uninit_self_reference_in_init; + } else { + // Local variables will be handled by the CFG analysis. + return; } S.DiagRuntimeBehavior(DRE->getLocStart(), DRE, diff --git a/clang/test/SemaCXX/uninitialized.cpp b/clang/test/SemaCXX/uninitialized.cpp index 018b1feba96..f02357419f5 100644 --- a/clang/test/SemaCXX/uninitialized.cpp +++ b/clang/test/SemaCXX/uninitialized.cpp @@ -86,7 +86,6 @@ void test_stuff () { int aa = (ref(aa) += 10); // expected-warning {{variable 'aa' is uninitialized when used within its own initialization}} int bb = bb ? x : y; // expected-warning {{variable 'bb' is uninitialized when used within its own initialization}} - for (;;) { int a = a; // no-warning: used to signal intended lack of initialization. int b = b + 1; // expected-warning {{variable 'b' is uninitialized when used within its own initialization}} @@ -124,6 +123,50 @@ void test_stuff () { } } +void test_comma() { + int a; // expected-note {{initialize the variable 'a' to silence this warning}} + int b = (a, a ?: 2); // expected-warning {{variable 'a' is uninitialized when used here}} + int c = (a, a, b, c); // expected-warning {{variable 'c' is uninitialized when used within its own initialization}} + int d; // expected-note {{initialize the variable 'd' to silence this warning}} + int e = (foo(d), e, b); // expected-warning {{variable 'd' is uninitialized when used here}} + int f; // expected-note {{initialize the variable 'f' to silence this warning}} + f = f + 1, 2; // expected-warning {{variable 'f' is uninitialized when used here}} + int h; + int g = (h, g, 2); // no-warning: h, g are evaluated but not used. +} + +namespace member_ptr { +struct A { + int x; + int y; + A(int x) : x{x} {} +}; + +void test_member_ptr() { + int A::* px = &A::x; + A a{a.*px}; // expected-warning {{variable 'a' is uninitialized when used within its own initialization}} + A b = b; // expected-warning {{variable 'b' is uninitialized when used within its own initialization}} +} +} + +namespace const_ptr { +void foo(int *a); +void bar(const int *a); +void foobar(const int **a); + +void test_const_ptr() { + int a; + int b; // expected-note {{initialize the variable 'b' to silence this warning}} + foo(&a); + bar(&b); + b = a + b; // expected-warning {{variable 'b' is uninitialized when used here}} + int *ptr; //expected-note {{initialize the variable 'ptr' to silence this warning}} + const int *ptr2; + foo(ptr); // expected-warning {{variable 'ptr' is uninitialized when used here}} + foobar(&ptr2); +} +} + // Also test similar constructs in a field's initializer. struct S { int x; |