[cfe-commits] Patch for PR5273
John McCall
rjmccall at apple.com
Tue Oct 27 11:58:30 PDT 2009
static bool IsCXXThisExpr(const Stmt* S) {
+ const CXXThisExpr* ThisExpr = dyn_cast<CXXThisExpr>(S);
+ if (ThisExpr) {
+ return true;
+ }
+ bool found = false;
+ for (Stmt::const_child_iterator it = S->child_begin();
+ it != S->child_end() && found == false;
+ ++it) {
+ found = IsCXXThisExpr(*it);
+ }
+ return found;
+}
This logic is still way too aggressive. The warning should only trigger
if we're pretty sure the result of the base expression will be the
'this' object; we cannot assume that of arbitrary expressions involving
'this'. The correct strategy is to start with the minimal possible case
— here, isa<CXXThisExpr>(S). If there are cases that doesn't cover that
you're certain you want to warn about, then add those in specifically —
for example, casts of this (or casts of casts of this, etc.) — and just
keep whitelisting expressions until you're happy with the warnings
you've got.
I would write this function as:
static bool IsCXXThisExpr(const Expr* E) {
while (const CastExpr *CE = dyn_cast<CastExpr>(E))
E = CE->getSubExpr();
return isa<CXXThisExpr>(E);
}
Relatedly, you should add more negative cases to your test cases: cases
where we really shouldn't generate the warning because we can't know
what's happening. For example:
class A {
int B;
A() : B((this-1)->B + 1) {} // should not warn
};
+ // Exception #1: We may be in a copy-constructor. To eliminate this
+ // possibility, check if this field is rooted in "this". If not, this
+ // field probably belongs to some other record.
+ // e.g. Foo(const Foo& rhs) : A(rhs.A) {}
+ if (!IsCXXThisExpr(S)) {
+ // In a copy-constructor; not a warning.
+ return false;
+ }
This isn't specific to copy constructors; please change the comment to
not lead with that.
+ // Exception #2: A reference operator is valid on an uninitialized field.
+ // e.g. Foo() : A(SomeWrapperOrDecorator(&A)) { }
+ if (PrevS) {
+ const UnaryOperator* UE = dyn_cast<UnaryOperator>(PrevS);
+ if (UE && UE->getOpcode() == UnaryOperator::AddrOf) {
+ // Field was preceded by '&' unary operator, which is perfectly valid.
+ return false;
+ }
+ }
This catches the case where we're taking the address of the argument,
but not the case where we're passing it by reference argument to some
function.
+ // A sanity check: ensure no args contain the member itself, as it is not yet
+ // initialized. e.g. foo(foo) or foo(DoXyz(foo)).
+ for (unsigned i = 0; i < NumArgs; ++i) {
+ SourceLocation L;
+ if (InitExprContainsUninitializedFields(Args[i], NULL, Member, &L)) {
+ Diag(L, diag::warn_field_is_uninit);
+ }
+ }
This can still warn twice for a single initializer if the initializer
has multiple arguments, e.g.
A() : B(B, B) {}
Please fix this and add a test to make sure this only warns once.
Otherwise looks pretty good! Thanks for taking this on.
John.
More information about the cfe-commits
mailing list