Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -288,6 +288,7 @@ def IncrementBool : DiagGroup<"increment-bool", [DeprecatedIncrementBool]>; def InfiniteRecursion : DiagGroup<"infinite-recursion">; def PureVirtualCallFromCtorDtor: DiagGroup<"call-to-pure-virtual-from-ctor-dtor">; +def VirtualCallFromCtorDtor: DiagGroup<"call-to-virtual-from-ctor-dtor">; def GNUImaginaryConstant : DiagGroup<"gnu-imaginary-constant">; def IgnoredQualifiers : DiagGroup<"ignored-qualifiers">; def : DiagGroup<"import">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -1625,6 +1625,12 @@ "overrides of %0 in subclasses are not available in the " "%select{constructor|destructor}1 of %2">, InGroup; +def warn_call_to_virtual_member_function_from_ctor_dtor : Warning< + "call to virtual member function %0 from %select{constructor|destructor}1 of %2 " + "will not call overridden implementations">, InGroup; +def note_call_to_virtual_member_function_from_ctor_dtor_silence : Note< + "qualify the call to silence this warning">; + def select_special_member_kind : TextSubstitution< "%select{default constructor|copy constructor|move constructor|" "copy assignment operator|move assignment operator|destructor}0">; Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -13071,6 +13071,36 @@ } } + if ((isa(CurContext) || + isa(CurContext)) && + isa(MemExpr->getBase()->IgnoreParenCasts()) && + TheCall->getMethodDecl()->isVirtual() && + !MemExpr->hasQualifier() && + // We already have a warning for pure virtual functions above, so don't + // consider them here + !TheCall->getMethodDecl()->isPure()) { + const CXXMethodDecl *CallMD = TheCall->getMethodDecl(); + // dynamic casting CurnContext to CXXMethodDecl here is fine since we know + // from the check above that CurnContext is either CXXConstructorDecl or + // CXXDestructorDecl, which both inherit from CXXMethodDecl. + const CXXMethodDecl *ContextMD = dyn_cast(CurContext); + assert(ContextMD && "CurContext was expected be a method declaration"); + const CXXRecordDecl *ContextRD = ContextMD->getParent(); + // A final class cannot be derived from, so issue a warning only if not + // final + if (ContextRD->isPolymorphic() && !ContextRD->hasAttr()) { + Diag(MemExpr->getBeginLoc(), + diag::warn_call_to_virtual_member_function_from_ctor_dtor) + << CallMD->getDeclName() << isa(CurContext) + << ContextMD->getParent()->getDeclName(); + Diag(MemExpr->getBeginLoc(), + diag::note_call_to_virtual_member_function_from_ctor_dtor_silence) + << FixItHint::CreateInsertion( + MemExpr->getBeginLoc(), + ContextMD->getParent()->getDeclName().getAsString() + "::"); + } + } + if (CXXDestructorDecl *DD = dyn_cast(TheCall->getMethodDecl())) { // a->A::f() doesn't go through the vtable, except in AppleKext mode. Index: test/Analysis/ctor.mm =================================================================== --- test/Analysis/ctor.mm +++ test/Analysis/ctor.mm @@ -1,7 +1,7 @@ -// RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -DI386 -analyzer-checker=core,debug.ExprInspection -fobjc-arc -analyzer-config c++-inlining=constructors -Wno-null-dereference -std=c++11 -verify -analyzer-config eagerly-assume=false %s -// RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -DI386 -analyzer-checker=core,debug.ExprInspection -fobjc-arc -analyzer-config c++-inlining=constructors -Wno-null-dereference -std=c++11 -verify -DTEST_INLINABLE_ALLOCATORS -analyzer-config eagerly-assume=false %s -// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin12 -analyzer-checker=core,debug.ExprInspection -fobjc-arc -analyzer-config c++-inlining=constructors -Wno-null-dereference -std=c++11 -verify -analyzer-config eagerly-assume=false %s -// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin12 -analyzer-checker=core,debug.ExprInspection -fobjc-arc -analyzer-config c++-inlining=constructors -Wno-null-dereference -std=c++11 -verify -DTEST_INLINABLE_ALLOCATORS -analyzer-config eagerly-assume=false %s +// RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -DI386 -analyzer-checker=core,debug.ExprInspection -fobjc-arc -analyzer-config c++-inlining=constructors -Wno-null-dereference -Wno-call-to-virtual-from-ctor-dtor -std=c++11 -verify -analyzer-config eagerly-assume=false %s +// RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -DI386 -analyzer-checker=core,debug.ExprInspection -fobjc-arc -analyzer-config c++-inlining=constructors -Wno-null-dereference -Wno-call-to-virtual-from-ctor-dtor -std=c++11 -verify -DTEST_INLINABLE_ALLOCATORS -analyzer-config eagerly-assume=false %s +// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin12 -analyzer-checker=core,debug.ExprInspection -fobjc-arc -analyzer-config c++-inlining=constructors -Wno-null-dereference -Wno-call-to-virtual-from-ctor-dtor -std=c++11 -verify -analyzer-config eagerly-assume=false %s +// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin12 -analyzer-checker=core,debug.ExprInspection -fobjc-arc -analyzer-config c++-inlining=constructors -Wno-null-dereference -Wno-call-to-virtual-from-ctor-dtor -std=c++11 -verify -DTEST_INLINABLE_ALLOCATORS -analyzer-config eagerly-assume=false %s #include "Inputs/system-header-simulator-cxx.h" Index: test/Analysis/dtor.cpp =================================================================== --- test/Analysis/dtor.cpp +++ test/Analysis/dtor.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,debug.ExprInspection,cplusplus -analyzer-config c++-inlining=destructors -Wno-null-dereference -Wno-inaccessible-base -verify -analyzer-config eagerly-assume=false %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,debug.ExprInspection,cplusplus -analyzer-config c++-inlining=destructors -Wno-null-dereference -Wno-inaccessible-base -Wno-call-to-virtual-from-ctor-dtor -verify -analyzer-config eagerly-assume=false %s void clang_analyzer_eval(bool); void clang_analyzer_checkInlined(bool); Index: test/Analysis/virtualcall.cpp =================================================================== --- test/Analysis/virtualcall.cpp +++ test/Analysis/virtualcall.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-output=text -verify -std=c++11 %s +// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-output=text -Wno-call-to-virtual-from-ctor-dtor -verify -std=c++11 %s -// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-config optin.cplusplus.VirtualCall:PureOnly=true -DPUREONLY=1 -analyzer-output=text -verify -std=c++11 %s +// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-config optin.cplusplus.VirtualCall:PureOnly=true -DPUREONLY=1 -analyzer-output=text -Wno-call-to-virtual-from-ctor-dtor -verify -std=c++11 %s #include "virtualcall.h" Index: test/SemaCXX/warn-virtual-call-from-ctor-dtor.cpp =================================================================== --- /dev/null +++ test/SemaCXX/warn-virtual-call-from-ctor-dtor.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 %s -fsyntax-only -verify -Wcall-to-virtual-from-ctor-dtor +struct A { + A() { f(); } // expected-warning {{call to virtual member function 'f' from constructor of 'A' will not call overridden implementations}} expected-note {{qualify the call to silence this warning}} + ~A() { f(); } // expected-warning {{call to virtual member function 'f' from destructor of 'A' will not call overridden implementations}} expected-note {{qualify the call to silence this warning}} + + virtual void f() {} +}; + +// Warns in classes inheriting from A too +struct B : A { + B() { f(); } // expected-warning {{call to virtual member function 'f' from constructor of 'B' will not call overridden implementations}} expected-note {{qualify the call to silence this warning}} + ~B() { f(); } // expected-warning {{call to virtual member function 'f' from destructor of 'B' will not call overridden implementations}} expected-note {{qualify the call to silence this warning}} + void f() {} +}; + +// Don't warn if the class if final +struct C final : A { + C() { f(); } + ~C() { f(); } +}; + +// Don't warn (or note) when calling the function on a pointer. +struct D : A { + A *a; + D() { a->f(); }; + ~D() { a->f(); }; +}; + +// Don't warn if the call is fully qualified. +struct E { + virtual void f() = 0; + E() { + E::f(); + } +};