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
This commit is contained in:
Manuel Klimek 2015-03-03 14:54:25 +00:00
parent 2d29340095
commit 27ee25f738
3 changed files with 95 additions and 14 deletions

View File

@ -14,6 +14,7 @@
#include "clang/AST/ASTContext.h" #include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h" #include "clang/AST/Attr.h"
#include "clang/AST/Decl.h" #include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/StmtVisitor.h" #include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/Analyses/PostOrderCFGView.h" #include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/Analyses/UninitializedValues.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->isExceptionVariable() && !vd->isInitCapture() &&
vd->getDeclContext() == dc) { vd->getDeclContext() == dc) {
QualType ty = vd->getType(); QualType ty = vd->getType();
return ty->isScalarType() || ty->isVectorType(); return ty->isScalarType() || ty->isVectorType() || ty->isRecordType();
} }
return false; return false;
} }
@ -347,6 +348,7 @@ public:
} }
static const DeclRefExpr *getSelfInitExpr(VarDecl *VD) { static const DeclRefExpr *getSelfInitExpr(VarDecl *VD) {
if (VD->getType()->isRecordType()) return nullptr;
if (Expr *Init = VD->getInit()) { if (Expr *Init = VD->getInit()) {
const DeclRefExpr *DRE const DeclRefExpr *DRE
= dyn_cast<DeclRefExpr>(stripCasts(VD->getASTContext(), Init)); = dyn_cast<DeclRefExpr>(stripCasts(VD->getASTContext(), Init));
@ -376,10 +378,26 @@ void ClassifyRefs::classify(const Expr *E, Class C) {
return; 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 (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); classify(BO->getRHS(), C);
return; return;
default:
return;
}
} }
FindVarResult Var = findVar(E, DC); FindVarResult Var = findVar(E, DC);
@ -404,7 +422,7 @@ void ClassifyRefs::VisitBinaryOperator(BinaryOperator *BO) {
// use. // use.
if (BO->isCompoundAssignmentOp()) if (BO->isCompoundAssignmentOp())
classify(BO->getLHS(), Use); classify(BO->getLHS(), Use);
else if (BO->getOpcode() == BO_Assign) else if (BO->getOpcode() == BO_Assign || BO->getOpcode() == BO_Comma)
classify(BO->getLHS(), Ignore); classify(BO->getLHS(), Ignore);
} }
@ -415,25 +433,40 @@ void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) {
classify(UO->getSubExpr(), Use); classify(UO->getSubExpr(), Use);
} }
static bool isPointerToConst(const QualType &QT) {
return QT->isAnyPointerType() && QT->getPointeeType().isConstQualified();
}
void ClassifyRefs::VisitCallExpr(CallExpr *CE) { void ClassifyRefs::VisitCallExpr(CallExpr *CE) {
// Classify arguments to std::move as used. // Classify arguments to std::move as used.
if (CE->getNumArgs() == 1) { if (CE->getNumArgs() == 1) {
if (FunctionDecl *FD = CE->getDirectCallee()) { if (FunctionDecl *FD = CE->getDirectCallee()) {
if (FD->isInStdNamespace() && FD->getIdentifier() && if (FD->isInStdNamespace() && FD->getIdentifier() &&
FD->getIdentifier()->isStr("move")) { FD->getIdentifier()->isStr("move")) {
// RecordTypes are handled in SemaDeclCXX.cpp.
if (!CE->getArg(0)->getType()->isRecordType())
classify(CE->getArg(0), Use); classify(CE->getArg(0), Use);
return; return;
} }
} }
} }
// If a value is passed by const reference to a function, we should not assume // If a value is passed by const pointer or by const reference to a function,
// that it is initialized by the call, and we conservatively do not assume // we should not assume that it is initialized by the call, and we
// that it is used. // conservatively do not assume that it is used.
for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end();
I != E; ++I) I != E; ++I) {
if ((*I)->getType().isConstQualified() && (*I)->isGLValue()) if ((*I)->isGLValue()) {
classify(*I, Ignore); 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) { void ClassifyRefs::VisitCastExpr(CastExpr *CE) {

View File

@ -8582,8 +8582,13 @@ namespace {
diag = diag::warn_uninit_self_reference_in_reference_init; diag = diag::warn_uninit_self_reference_in_reference_init;
} else if (cast<VarDecl>(OrigDecl)->isStaticLocal()) { } else if (cast<VarDecl>(OrigDecl)->isStaticLocal()) {
diag = diag::warn_static_self_reference_in_init; 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; diag = diag::warn_uninit_self_reference_in_init;
} else {
// Local variables will be handled by the CFG analysis.
return;
} }
S.DiagRuntimeBehavior(DRE->getLocStart(), DRE, S.DiagRuntimeBehavior(DRE->getLocStart(), DRE,

View File

@ -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 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}} int bb = bb ? x : y; // expected-warning {{variable 'bb' is uninitialized when used within its own initialization}}
for (;;) { for (;;) {
int a = a; // no-warning: used to signal intended lack of initialization. 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}} 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. // Also test similar constructs in a field's initializer.
struct S { struct S {
int x; int x;