Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -1509,6 +1509,9 @@ def note_uninit_in_this_constructor : Note< "during field initialization in %select{this|the implicit default}0 " "constructor">; +def warn_member_call_before_bases_init : Warning< + "member function %0 called before all base classes were initialized">, + InGroup; def warn_static_self_reference_in_init : Warning< "static variable %0 is suspiciously used within its own initialization">, InGroup; Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -2638,6 +2638,44 @@ } } // namespace +namespace { +class DiagnoseMemberCallVisitor + : public RecursiveASTVisitor { + Sema &S; + +public: + DiagnoseMemberCallVisitor(Sema &SemaRef) : S(SemaRef) {} + + bool VisitCXXMemberCallExpr(CXXMemberCallExpr *E) { + const Expr *IA = E->getImplicitObjectArgument(); + if (IA->isImplicitCXXThis() || isa(IA)) { + S.Diag(E->getExprLoc(), diag::warn_member_call_before_bases_init) + << E->getDirectCallee(); + } + return true; + } +}; + +// Diagnoses method calls before all base classes are initialized in a +// mem-initializer-list which is undefined behaviour. +// Source: C++03 12.6.2p8; C++11 12.6.2p13. +static void +DiagnoseUndefinedMemberFunctionCalls(Sema &SemaRef, + const CXXConstructorDecl *Constructor) { + if (SemaRef.getDiagnostics().isIgnored( + diag::warn_member_call_before_bases_init, Constructor->getLocation())) + return; + + DiagnoseMemberCallVisitor Visitor(SemaRef); + for (const auto *I : Constructor->inits()) { + if (!I->isBaseInitializer()) + continue; + Expr *E = I->getInit(); + Visitor.TraverseStmt(E); + } +} +} // namespace + /// \brief Enter a new C++ default initializer scope. After calling this, the /// caller must call \ref ActOnFinishCXXInClassMemberInitializer, even if /// parsing or instantiating the initializer failed. @@ -4242,6 +4280,7 @@ SetCtorInitializers(Constructor, AnyErrors, MemInits); DiagnoseUninitializedFields(*this, Constructor); + DiagnoseUndefinedMemberFunctionCalls(*this, Constructor); } void Index: test/SemaCXX/member-call-before-bases-init.cpp =================================================================== --- test/SemaCXX/member-call-before-bases-init.cpp +++ test/SemaCXX/member-call-before-bases-init.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fsyntax-only -Wuninitialized -verify %s + +class A { +public: + A(int); +}; + +int g(int); + +class B : public A { + int j; + +public: + int f(); + B() : A(g(f())), // expected-warning{{member function 'f' called before all base classes were initialized}} + j(f()) {} // no-warning + + B(int) : A(this->f()), // expected-warning{{member function 'f' called before all base classes were initialized}} + j(0) {} +}; + +class C { +public: + C(int); +}; + +class D : public B, C { + int i; + +public: + D() : C(f()), // expected-warning{{member function 'f' called before all base classes were initialized}} + i(f()) {} // no-warning +}; Index: test/SemaCXX/uninitialized.cpp =================================================================== --- test/SemaCXX/uninitialized.cpp +++ test/SemaCXX/uninitialized.cpp @@ -1323,6 +1323,7 @@ B(int (*)[4]) : A(foo()) {} // expected-warning@-1 {{base_class_access::A' is uninitialized when used here to access 'base_class_access::A::foo'}} + // expected-warning@-2 {{member function 'foo' called before all base classes were initialized}} }; struct C { @@ -1338,6 +1339,7 @@ D(int (*)[4]) : C(foo()) {} // expected-warning@-1 {{base_class_access::A' is uninitialized when used here to access 'base_class_access::A::foo'}} + // expected-warning@-2 {{member function 'foo' called before all base classes were initialized}} }; }