mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-10 18:11:19 +00:00
Modify the uninitialized field visitor to detect uninitialized use across the
fields in the class. This allows a better checking of member intiailizers and in class initializers in regards to initialization ordering. For instance, this code will now produce warnings: class A { int x; int y; A() : x(y) {} // y is initialized after x, warn here A(int): y(x) {} // default initialization of leaves x uninitialized, warn here }; Several test cases were updated with -Wno-uninitialized to silence this warning. llvm-svn: 191068
This commit is contained in:
parent
b8c65f0136
commit
406e65c8d1
@ -1370,6 +1370,8 @@ def warn_field_is_uninit : Warning<"field %0 is uninitialized when used here">,
|
||||
def warn_reference_field_is_uninit : Warning<
|
||||
"reference %0 is not yet bound to a value when used here">,
|
||||
InGroup<Uninitialized>;
|
||||
def note_uninit_in_this_constructor : Note<
|
||||
"during field initialization in this constructor">;
|
||||
def warn_static_self_reference_in_init : Warning<
|
||||
"static variable %0 is suspiciously used within its own initialization">,
|
||||
InGroup<UninitializedStaticSelfInit>;
|
||||
|
@ -2081,20 +2081,37 @@ namespace {
|
||||
class UninitializedFieldVisitor
|
||||
: public EvaluatedExprVisitor<UninitializedFieldVisitor> {
|
||||
Sema &S;
|
||||
// If VD is null, this visitor will only update the Decls set.
|
||||
ValueDecl *VD;
|
||||
bool isReferenceType;
|
||||
// List of Decls to generate a warning on.
|
||||
llvm::SmallPtrSet<ValueDecl*, 4> &Decls;
|
||||
bool WarnOnSelfReference;
|
||||
// If non-null, add a note to the warning pointing back to the constructor.
|
||||
const CXXConstructorDecl *Constructor;
|
||||
public:
|
||||
typedef EvaluatedExprVisitor<UninitializedFieldVisitor> Inherited;
|
||||
UninitializedFieldVisitor(Sema &S, ValueDecl *VD) : Inherited(S.Context),
|
||||
S(S) {
|
||||
if (IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(VD))
|
||||
this->VD = IFD->getAnonField();
|
||||
else
|
||||
this->VD = VD;
|
||||
isReferenceType = this->VD->getType()->isReferenceType();
|
||||
UninitializedFieldVisitor(Sema &S, ValueDecl *VD,
|
||||
llvm::SmallPtrSet<ValueDecl*, 4> &Decls,
|
||||
bool WarnOnSelfReference,
|
||||
const CXXConstructorDecl *Constructor)
|
||||
: Inherited(S.Context), S(S), VD(VD), isReferenceType(false), Decls(Decls),
|
||||
WarnOnSelfReference(WarnOnSelfReference), Constructor(Constructor) {
|
||||
// When VD is null, this visitor is used to detect initialization of other
|
||||
// fields.
|
||||
if (VD) {
|
||||
if (IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(VD))
|
||||
this->VD = IFD->getAnonField();
|
||||
else
|
||||
this->VD = VD;
|
||||
isReferenceType = this->VD->getType()->isReferenceType();
|
||||
}
|
||||
}
|
||||
|
||||
void HandleMemberExpr(MemberExpr *ME, bool CheckReferenceOnly) {
|
||||
if (!VD)
|
||||
return;
|
||||
|
||||
if (CheckReferenceOnly && !isReferenceType)
|
||||
return;
|
||||
|
||||
@ -2122,15 +2139,38 @@ namespace {
|
||||
if (!isa<CXXThisExpr>(Base))
|
||||
return;
|
||||
|
||||
if (VD == FieldME->getMemberDecl()) {
|
||||
ValueDecl* FoundVD = FieldME->getMemberDecl();
|
||||
|
||||
if (VD == FoundVD) {
|
||||
if (!WarnOnSelfReference)
|
||||
return;
|
||||
|
||||
unsigned diag = isReferenceType
|
||||
? diag::warn_reference_field_is_uninit
|
||||
: diag::warn_field_is_uninit;
|
||||
S.Diag(FieldME->getExprLoc(), diag) << VD;
|
||||
if (Constructor)
|
||||
S.Diag(Constructor->getLocation(),
|
||||
diag::note_uninit_in_this_constructor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (CheckReferenceOnly)
|
||||
return;
|
||||
|
||||
if (Decls.count(FoundVD)) {
|
||||
S.Diag(FieldME->getExprLoc(), diag::warn_field_is_uninit) << FoundVD;
|
||||
if (Constructor)
|
||||
S.Diag(Constructor->getLocation(),
|
||||
diag::note_uninit_in_this_constructor);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void HandleValue(Expr *E) {
|
||||
if (!VD)
|
||||
return;
|
||||
|
||||
E = E->IgnoreParens();
|
||||
|
||||
if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
|
||||
@ -2180,7 +2220,7 @@ namespace {
|
||||
}
|
||||
|
||||
void VisitCXXConstructExpr(CXXConstructExpr *E) {
|
||||
if (E->getNumArgs() == 1)
|
||||
if (E->getConstructor()->isCopyConstructor())
|
||||
if (ImplicitCastExpr* ICE = dyn_cast<ImplicitCastExpr>(E->getArg(0)))
|
||||
if (ICE->getCastKind() == CK_NoOp)
|
||||
if (MemberExpr *ME = dyn_cast<MemberExpr>(ICE->getSubExpr()))
|
||||
@ -2196,11 +2236,27 @@ namespace {
|
||||
|
||||
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<MemberExpr>(E->getLHS()))
|
||||
if (FieldDecl *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()))
|
||||
Decls.erase(FD);
|
||||
|
||||
Inherited::VisitBinaryOperator(E);
|
||||
}
|
||||
};
|
||||
static void CheckInitExprContainsUninitializedFields(Sema &S, Expr *E,
|
||||
ValueDecl *VD) {
|
||||
static void CheckInitExprContainsUninitializedFields(
|
||||
Sema &S, Expr *E, ValueDecl *VD, llvm::SmallPtrSet<ValueDecl*, 4> &Decls,
|
||||
bool WarnOnSelfReference, const CXXConstructorDecl *Constructor = 0) {
|
||||
if (Decls.size() == 0 && !WarnOnSelfReference)
|
||||
return;
|
||||
|
||||
if (E)
|
||||
UninitializedFieldVisitor(S, VD).Visit(E);
|
||||
UninitializedFieldVisitor(S, VD, Decls, WarnOnSelfReference, Constructor)
|
||||
.Visit(E);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@ -2252,11 +2308,6 @@ Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation InitLoc,
|
||||
|
||||
InitExpr = Init.release();
|
||||
|
||||
if (getDiagnostics().getDiagnosticLevel(diag::warn_field_is_uninit, InitLoc)
|
||||
!= DiagnosticsEngine::Ignored) {
|
||||
CheckInitExprContainsUninitializedFields(*this, InitExpr, FD);
|
||||
}
|
||||
|
||||
FD->setInClassInitializer(InitExpr);
|
||||
}
|
||||
|
||||
@ -3702,15 +3753,9 @@ bool CheckRedundantUnionInit(Sema &S,
|
||||
// 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.
|
||||
// FIXME: Warn about the case when other fields are used before being
|
||||
// initialized. For example, let this field be the i'th field. When
|
||||
// initializing the i'th field, throw a warning if any of the >= i'th
|
||||
// fields are used, as they are not yet initialized.
|
||||
// Right now we are only handling the case where the i'th field uses
|
||||
// itself in its initializer.
|
||||
// Also need to take into account that some fields may be initialized by
|
||||
// in-class initializers, see C++11 [class.base.init]p9.
|
||||
static void DiagnoseUnitializedFields(
|
||||
Sema &SemaRef, const CXXConstructorDecl *Constructor) {
|
||||
|
||||
@ -3720,19 +3765,72 @@ static void DiagnoseUnitializedFields(
|
||||
return;
|
||||
}
|
||||
|
||||
for (CXXConstructorDecl::init_const_iterator
|
||||
FieldInit = Constructor->init_begin(),
|
||||
const CXXRecordDecl *RD = Constructor->getParent();
|
||||
|
||||
// Holds fields that are uninitialized.
|
||||
llvm::SmallPtrSet<ValueDecl*, 4> UninitializedFields;
|
||||
|
||||
for (DeclContext::decl_iterator I = RD->decls_begin(), E = RD->decls_end();
|
||||
I != E; ++I) {
|
||||
if (FieldDecl *FD = dyn_cast<FieldDecl>(*I)) {
|
||||
UninitializedFields.insert(FD);
|
||||
} else if (IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(*I)) {
|
||||
UninitializedFields.insert(IFD->getAnonField());
|
||||
}
|
||||
}
|
||||
|
||||
// Fields already checked when processing the in class initializers.
|
||||
llvm::SmallPtrSet<ValueDecl*, 4>
|
||||
InClassUninitializedFields = UninitializedFields;
|
||||
|
||||
for (CXXConstructorDecl::init_const_iterator FieldInit =
|
||||
Constructor->init_begin(),
|
||||
FieldInitEnd = Constructor->init_end();
|
||||
FieldInit != FieldInitEnd; ++FieldInit) {
|
||||
|
||||
FieldDecl *FD = (*FieldInit)->getAnyMember();
|
||||
if (!FD)
|
||||
continue;
|
||||
FieldDecl *Field = (*FieldInit)->getAnyMember();
|
||||
Expr *InitExpr = (*FieldInit)->getInit();
|
||||
|
||||
if (!isa<CXXDefaultInitExpr>(InitExpr)) {
|
||||
CheckInitExprContainsUninitializedFields(SemaRef, InitExpr, FD);
|
||||
if (!Field) {
|
||||
CheckInitExprContainsUninitializedFields(
|
||||
SemaRef, InitExpr, 0, UninitializedFields,
|
||||
false/*WarnOnSelfReference*/);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CXXDefaultInitExpr *Default = dyn_cast<CXXDefaultInitExpr>(InitExpr)) {
|
||||
// This field is initialized with an in-class initailzer. Remove the
|
||||
// fields already checked to prevent duplicate warnings.
|
||||
llvm::SmallPtrSet<ValueDecl*, 4> DiffSet = UninitializedFields;
|
||||
for (llvm::SmallPtrSet<ValueDecl*, 4>::iterator
|
||||
I = InClassUninitializedFields.begin(),
|
||||
E = InClassUninitializedFields.end();
|
||||
I != E; ++I) {
|
||||
DiffSet.erase(*I);
|
||||
}
|
||||
CheckInitExprContainsUninitializedFields(
|
||||
SemaRef, Default->getExpr(), Field, DiffSet,
|
||||
DiffSet.count(Field), Constructor);
|
||||
|
||||
// Update the unitialized field sets.
|
||||
CheckInitExprContainsUninitializedFields(
|
||||
SemaRef, Default->getExpr(), 0, UninitializedFields,
|
||||
false);
|
||||
CheckInitExprContainsUninitializedFields(
|
||||
SemaRef, Default->getExpr(), 0, InClassUninitializedFields,
|
||||
false);
|
||||
} else {
|
||||
CheckInitExprContainsUninitializedFields(
|
||||
SemaRef, InitExpr, Field, UninitializedFields,
|
||||
UninitializedFields.count(Field));
|
||||
if (Expr* InClassInit = Field->getInClassInitializer()) {
|
||||
CheckInitExprContainsUninitializedFields(
|
||||
SemaRef, InClassInit, 0, InClassUninitializedFields,
|
||||
false);
|
||||
}
|
||||
}
|
||||
UninitializedFields.erase(Field);
|
||||
InClassUninitializedFields.erase(Field);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8057,6 +8155,56 @@ void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) {
|
||||
// Check that any explicitly-defaulted methods have exception specifications
|
||||
// compatible with their implicit exception specifications.
|
||||
CheckDelayedExplicitlyDefaultedMemberExceptionSpecs();
|
||||
|
||||
// Once all the member initializers are processed, perform checks to see if
|
||||
// any unintialized use is happeneing.
|
||||
if (getDiagnostics().getDiagnosticLevel(diag::warn_field_is_uninit,
|
||||
D->getLocation())
|
||||
== DiagnosticsEngine::Ignored)
|
||||
return;
|
||||
|
||||
CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D);
|
||||
if (!RD) return;
|
||||
|
||||
// Holds fields that are uninitialized.
|
||||
llvm::SmallPtrSet<ValueDecl*, 4> UninitializedFields;
|
||||
|
||||
// In the beginning, every field is uninitialized.
|
||||
for (DeclContext::decl_iterator I = RD->decls_begin(), E = RD->decls_end();
|
||||
I != E; ++I) {
|
||||
if (FieldDecl *FD = dyn_cast<FieldDecl>(*I)) {
|
||||
UninitializedFields.insert(FD);
|
||||
} else if (IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(*I)) {
|
||||
UninitializedFields.insert(IFD->getAnonField());
|
||||
}
|
||||
}
|
||||
|
||||
for (DeclContext::decl_iterator I = RD->decls_begin(), E = RD->decls_end();
|
||||
I != E; ++I) {
|
||||
FieldDecl *FD = dyn_cast<FieldDecl>(*I);
|
||||
if (!FD)
|
||||
if (IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(*I))
|
||||
FD = IFD->getAnonField();
|
||||
|
||||
if (!FD)
|
||||
continue;
|
||||
|
||||
Expr *InitExpr = FD->getInClassInitializer();
|
||||
if (!InitExpr) {
|
||||
// Uninitialized reference types will give an error.
|
||||
// Record types with an initializer are default initialized.
|
||||
QualType FieldType = FD->getType();
|
||||
if (FieldType->isReferenceType() || FieldType->isRecordType())
|
||||
UninitializedFields.erase(FD);
|
||||
continue;
|
||||
}
|
||||
|
||||
CheckInitExprContainsUninitializedFields(
|
||||
*this, InitExpr, FD, UninitializedFields,
|
||||
UninitializedFields.count(FD)/*WarnOnSelfReference*/);
|
||||
|
||||
UninitializedFields.erase(FD);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// RUN: %clang_cc1 -std=c++1y %s -verify
|
||||
// RUN: %clang_cc1 -Wno-uninitialized -std=c++1y %s -verify
|
||||
|
||||
// expected-no-diagnostics
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// RUN: %clang_cc1 -std=c++11 -verify %s
|
||||
// RUN: %clang_cc1 -Wno-uninitialized -std=c++11 -verify %s
|
||||
|
||||
template<int> struct c { c(int) = delete; typedef void val; operator int() const; };
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// RUN: %clang_cc1 %s -std=c++11 -fsyntax-only -verify
|
||||
// RUN: %clang_cc1 %s -Wno-uninitialized -std=c++11 -fsyntax-only -verify
|
||||
|
||||
struct A {
|
||||
constexpr A() : a(b + 1), b(a + 1) {} // expected-note {{outside its lifetime}}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wno-error=static-float-init %s
|
||||
// RUN: %clang_cc1 -Wno-uninitialized -fsyntax-only -verify -std=c++11 -Wno-error=static-float-init %s
|
||||
|
||||
int vs = 0;
|
||||
|
||||
|
@ -340,7 +340,10 @@ namespace {
|
||||
};
|
||||
|
||||
struct E {
|
||||
int a, b, c;
|
||||
int b = 1;
|
||||
int c = 1;
|
||||
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 (*)[3]) : a(b ? (a) : c) {} // expected-warning {{field 'a' is uninitialized when used here}}
|
||||
@ -459,7 +462,7 @@ namespace in_class_initializers {
|
||||
};
|
||||
|
||||
struct U {
|
||||
U() : a(b + 1), b(a + 1) {} // FIXME: Warn here.
|
||||
U() : a(b + 1), b(a + 1) {} // expected-warning{{field 'b' is uninitialized when used here}}
|
||||
int a = 42; // Note: because a and b are in the member initializer list, these initializers are ignored.
|
||||
int b = 1;
|
||||
};
|
||||
@ -561,3 +564,107 @@ namespace record_fields {
|
||||
A a9 = normal(a9); // expected-warning {{uninitialized}}
|
||||
};
|
||||
}
|
||||
|
||||
namespace cross_field_warnings {
|
||||
struct A {
|
||||
int a, b;
|
||||
A() {}
|
||||
A(char (*)[1]) : b(a) {} // expected-warning{{field 'a' is uninitialized when used here}}
|
||||
A(char (*)[2]) : a(b) {} // expected-warning{{field 'b' is uninitialized when used here}}
|
||||
};
|
||||
|
||||
struct B {
|
||||
int a = b; // expected-warning{{field 'b' is uninitialized when used here}}
|
||||
int b;
|
||||
};
|
||||
|
||||
struct C {
|
||||
int a;
|
||||
int b = a; // expected-warning{{field 'a' is uninitialized when used here}}
|
||||
C(char (*)[1]) : a(5) {}
|
||||
C(char (*)[2]) {}
|
||||
};
|
||||
|
||||
struct D {
|
||||
int a;
|
||||
int &b;
|
||||
int &c = a;
|
||||
int d = b;
|
||||
D() : b(a) {}
|
||||
};
|
||||
|
||||
struct E {
|
||||
int a;
|
||||
int get();
|
||||
static int num();
|
||||
E() {}
|
||||
E(int) {}
|
||||
};
|
||||
|
||||
struct F {
|
||||
int a;
|
||||
E e;
|
||||
int b;
|
||||
F(char (*)[1]) : a(e.get()) {} // expected-warning{{field 'e' is uninitialized when used here}}
|
||||
F(char (*)[2]) : a(e.num()) {}
|
||||
F(char (*)[3]) : e(a) {} // expected-warning{{field 'a' is uninitialized when used here}}
|
||||
F(char (*)[4]) : a(4), e(a) {}
|
||||
F(char (*)[5]) : e(b) {} // expected-warning{{field 'b' is uninitialized when used here}}
|
||||
F(char (*)[6]) : e(b), b(4) {} // expected-warning{{field 'b' is uninitialized when used here}}
|
||||
};
|
||||
|
||||
struct G {
|
||||
G(const A&) {};
|
||||
};
|
||||
|
||||
struct H {
|
||||
A a1;
|
||||
G g;
|
||||
A a2;
|
||||
H() : g(a1) {}
|
||||
H(int) : g(a2) {}
|
||||
};
|
||||
|
||||
struct I {
|
||||
I(int*) {}
|
||||
};
|
||||
|
||||
struct J : public I {
|
||||
int *a;
|
||||
int *b;
|
||||
int c;
|
||||
J() : I((a = new int(5))), b(a), c(*a) {}
|
||||
};
|
||||
|
||||
struct K {
|
||||
int a = (b = 5);
|
||||
int b = b + 5;
|
||||
};
|
||||
|
||||
struct L {
|
||||
int a = (b = 5);
|
||||
int b = b + 5; // expected-warning{{field 'b' is uninitialized when used here}}
|
||||
L() : a(5) {} // expected-note{{during field initialization in this constructor}}
|
||||
};
|
||||
|
||||
struct M { };
|
||||
|
||||
struct N : public M {
|
||||
int a;
|
||||
int b;
|
||||
N() : b(a) { } // expected-warning{{field 'a' is uninitialized when used here}}
|
||||
};
|
||||
|
||||
struct O {
|
||||
int x = 42;
|
||||
int get() { return x; }
|
||||
};
|
||||
|
||||
struct P {
|
||||
O o;
|
||||
int x = o.get();
|
||||
P() : x(o.get()) { }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only -Wunused-private-field -Wused-but-marked-unused -verify -std=c++11 %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -Wunused-private-field -Wused-but-marked-unused -Wno-uninitialized -verify -std=c++11 %s
|
||||
|
||||
class NotFullyDefined {
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user