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:
parent
2d29340095
commit
27ee25f738
|
@ -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;
|
||||
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")) {
|
||||
// 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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue