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/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,12 +378,28 @@ void ClassifyRefs::classify(const Expr *E, Class C) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
|
if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
|
||||||
if (BO->getOpcode() == BO_Comma)
|
if (VarDecl *VD = dyn_cast<VarDecl>(ME->getMemberDecl())) {
|
||||||
classify(BO->getRHS(), C);
|
if (!VD->isStaticDataMember())
|
||||||
|
classify(ME->getBase(), C);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
|
||||||
|
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);
|
FindVarResult Var = findVar(E, DC);
|
||||||
if (const DeclRefExpr *DRE = Var.getDeclRefExpr())
|
if (const DeclRefExpr *DRE = Var.getDeclRefExpr())
|
||||||
Classification[DRE] = std::max(Classification[DRE], C);
|
Classification[DRE] = std::max(Classification[DRE], C);
|
||||||
|
@ -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")) {
|
||||||
classify(CE->getArg(0), Use);
|
// RecordTypes are handled in SemaDeclCXX.cpp.
|
||||||
|
if (!CE->getArg(0)->getType()->isRecordType())
|
||||||
|
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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue