[clang-tools-extra] r329448 - [clang-tidy] Check if grand-..parent's virtual method was called instead of overridden parent's.

Mikael Holmén via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 9 00:20:10 PDT 2018


Hi Zivony,

 From this patch:

 > +void ParentVirtualCallCheck::check(const MatchFinder::MatchResult 
&Result) {
 > +  const auto *MatchedDecl = 
Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
 > +  assert(MatchedDecl);
 > +


The code above yields a warning when compiled without asserts since 
MatchedDecl is not used then:

../tools/clang/tools/extra/clang-tidy/bugprone/ParentVirtualCallCheck.cpp:101:15: 
error: unused variable 'MatchedDecl' [-Werror,-Wunused-variable]
   const auto *MatchedDecl = 
Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
               ^
1 error generated.

Regards,
Mikael



On 04/06/2018 10:02 PM, Zinovy Nis via cfe-commits wrote:
> Author: zinovy.nis
> Date: Fri Apr  6 13:02:50 2018
> New Revision: 329448
> 
> URL: http://llvm.org/viewvc/llvm-project?rev=329448&view=rev
> Log:
> [clang-tidy] Check if grand-..parent's virtual method was called instead of overridden parent's.
> 
> class A {...int virtual foo() {...}...};
> class B: public A {...int foo() override {...}...};
> class C: public B {...int foo() override {... A::foo()...}};
>                                                ^^^^^^^^ warning: qualified name A::foo refers to a member overridden in subclass; did you mean 'B'? [bugprone-parent-virtual-call]
> 
> Differential Revision: https://reviews.llvm.org/D44295
> 
> Added:
>      clang-tools-extra/trunk/clang-tidy/bugprone/ParentVirtualCallCheck.cpp   (with props)
>      clang-tools-extra/trunk/clang-tidy/bugprone/ParentVirtualCallCheck.h   (with props)
>      clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-parent-virtual-call.rst   (with props)
>      clang-tools-extra/trunk/test/clang-tidy/bugprone-parent-virtual-call.cpp   (with props)
> Modified:
>      clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp
>      clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt
>      clang-tools-extra/trunk/docs/ReleaseNotes.rst
>      clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
> 
> Modified: clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp?rev=329448&r1=329447&r2=329448&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp (original)
> +++ clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp Fri Apr  6 13:02:50 2018
> @@ -28,6 +28,7 @@
>   #include "MisplacedWideningCastCheck.h"
>   #include "MoveForwardingReferenceCheck.h"
>   #include "MultipleStatementMacroCheck.h"
> +#include "ParentVirtualCallCheck.h"
>   #include "SizeofContainerCheck.h"
>   #include "SizeofExpressionCheck.h"
>   #include "StringConstructorCheck.h"
> @@ -90,6 +91,8 @@ public:
>           "bugprone-move-forwarding-reference");
>       CheckFactories.registerCheck<MultipleStatementMacroCheck>(
>           "bugprone-multiple-statement-macro");
> +    CheckFactories.registerCheck<ParentVirtualCallCheck>(
> +        "bugprone-parent-virtual-call");
>       CheckFactories.registerCheck<SizeofContainerCheck>(
>           "bugprone-sizeof-container");
>       CheckFactories.registerCheck<SizeofExpressionCheck>(
> 
> Modified: clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt?rev=329448&r1=329447&r2=329448&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt (original)
> +++ clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt Fri Apr  6 13:02:50 2018
> @@ -20,6 +20,7 @@ add_clang_library(clangTidyBugproneModul
>     MisplacedWideningCastCheck.cpp
>     MoveForwardingReferenceCheck.cpp
>     MultipleStatementMacroCheck.cpp
> +  ParentVirtualCallCheck.cpp
>     SizeofContainerCheck.cpp
>     SizeofExpressionCheck.cpp
>     StringConstructorCheck.cpp
> 
> Added: clang-tools-extra/trunk/clang-tidy/bugprone/ParentVirtualCallCheck.cpp
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/ParentVirtualCallCheck.cpp?rev=329448&view=auto
> ==============================================================================
> --- clang-tools-extra/trunk/clang-tidy/bugprone/ParentVirtualCallCheck.cpp (added)
> +++ clang-tools-extra/trunk/clang-tidy/bugprone/ParentVirtualCallCheck.cpp Fri Apr  6 13:02:50 2018
> @@ -0,0 +1,154 @@
> +//===--- ParentVirtualCallCheck.cpp - clang-tidy---------------------------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "ParentVirtualCallCheck.h"
> +#include "clang/AST/ASTContext.h"
> +#include "clang/ASTMatchers/ASTMatchFinder.h"
> +#include "clang/Tooling/FixIt.h"
> +#include "llvm/ADT/STLExtras.h"
> +#include "llvm/ADT/SmallVector.h"
> +#include <cctype>
> +
> +using namespace clang::ast_matchers;
> +
> +namespace clang {
> +namespace tidy {
> +namespace bugprone {
> +
> +using BasesVector = llvm::SmallVector<const CXXRecordDecl *, 5>;
> +
> +static bool isParentOf(const CXXRecordDecl &Parent,
> +                       const CXXRecordDecl &ThisClass) {
> +  if (Parent.getCanonicalDecl() == ThisClass.getCanonicalDecl())
> +    return true;
> +  const auto ClassIter = llvm::find_if(ThisClass.bases(), [=](auto &Base) {
> +    auto *BaseDecl = Base.getType()->getAsCXXRecordDecl();
> +    assert(BaseDecl);
> +    return Parent.getCanonicalDecl() == BaseDecl->getCanonicalDecl();
> +  });
> +  return ClassIter != ThisClass.bases_end();
> +}
> +
> +static BasesVector getParentsByGrandParent(const CXXRecordDecl &GrandParent,
> +                                           const CXXRecordDecl &ThisClass,
> +                                           const CXXMethodDecl &MemberDecl) {
> +  BasesVector Result;
> +  for (const auto &Base : ThisClass.bases()) {
> +    const auto *BaseDecl = Base.getType()->getAsCXXRecordDecl();
> +    const CXXMethodDecl *ActualMemberDecl =
> +        MemberDecl.getCorrespondingMethodInClass(BaseDecl);
> +    if (!ActualMemberDecl)
> +      continue;
> +    // TypePtr is the nearest base class to ThisClass between ThisClass and
> +    // GrandParent, where MemberDecl is overridden. TypePtr is the class the
> +    // check proposes to fix to.
> +    const Type *TypePtr =
> +        ActualMemberDecl->getThisType(ActualMemberDecl->getASTContext())
> +            .getTypePtr();
> +    const CXXRecordDecl *RecordDeclType = TypePtr->getPointeeCXXRecordDecl();
> +    assert(RecordDeclType && "TypePtr is not a pointer to CXXRecordDecl!");
> +    if (RecordDeclType->getCanonicalDecl()->isDerivedFrom(&GrandParent))
> +      Result.emplace_back(RecordDeclType);
> +  }
> +
> +  return Result;
> +}
> +
> +static std::string getNameAsString(const NamedDecl *Decl) {
> +  std::string QualName;
> +  llvm::raw_string_ostream OS(QualName);
> +  PrintingPolicy PP(Decl->getASTContext().getPrintingPolicy());
> +  PP.SuppressUnwrittenScope = true;
> +  Decl->printQualifiedName(OS, PP);
> +  return OS.str();
> +}
> +
> +// Returns E as written in the source code. Used to handle 'using' and
> +// 'typedef'ed names of grand-parent classes.
> +static std::string getExprAsString(const clang::Expr &E,
> +                                   clang::ASTContext &AC) {
> +  std::string Text = tooling::fixit::getText(E, AC).str();
> +  Text.erase(llvm::remove_if(Text, std::isspace), Text.end());
> +  return Text;
> +}
> +
> +void ParentVirtualCallCheck::registerMatchers(MatchFinder *Finder) {
> +  Finder->addMatcher(
> +      cxxMemberCallExpr(
> +          callee(memberExpr(hasDescendant(implicitCastExpr(
> +                                hasImplicitDestinationType(pointsTo(
> +                                    type(anything()).bind("castToType"))),
> +                                hasSourceExpression(cxxThisExpr(hasType(
> +                                    type(anything()).bind("thisType")))))))
> +                     .bind("member")),
> +          callee(cxxMethodDecl(isVirtual())))
> +          .bind("call"),
> +      this);
> +}
> +
> +void ParentVirtualCallCheck::check(const MatchFinder::MatchResult &Result) {
> +  const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
> +  assert(MatchedDecl);
> +
> +  const auto *Member = Result.Nodes.getNodeAs<MemberExpr>("member");
> +  assert(Member);
> +
> +  if (!Member->getQualifier())
> +    return;
> +
> +  const auto *MemberDecl = cast<CXXMethodDecl>(Member->getMemberDecl());
> +
> +  const auto *ThisTypePtr = Result.Nodes.getNodeAs<PointerType>("thisType");
> +  assert(ThisTypePtr);
> +
> +  const auto *ThisType = ThisTypePtr->getPointeeCXXRecordDecl();
> +  assert(ThisType);
> +
> +  const auto *CastToTypePtr = Result.Nodes.getNodeAs<Type>("castToType");
> +  assert(CastToTypePtr);
> +
> +  const auto *CastToType = CastToTypePtr->getAsCXXRecordDecl();
> +  assert(CastToType);
> +
> +  if (isParentOf(*CastToType, *ThisType))
> +    return;
> +
> +  const BasesVector Parents =
> +      getParentsByGrandParent(*CastToType, *ThisType, *MemberDecl);
> +
> +  if (Parents.empty())
> +    return;
> +
> +  std::string ParentsStr;
> +  ParentsStr.reserve(30 * Parents.size());
> +  for (const CXXRecordDecl *Parent : Parents) {
> +    if (!ParentsStr.empty())
> +      ParentsStr.append(" or ");
> +    ParentsStr.append("'").append(getNameAsString(Parent)).append("'");
> +  }
> +
> +  assert(Member->getQualifierLoc().getSourceRange().getBegin().isValid());
> +  auto Diag = diag(Member->getQualifierLoc().getSourceRange().getBegin(),
> +                   "qualified name '%0' refers to a member overridden "
> +                   "in subclass%1; did you mean %2?")
> +              << getExprAsString(*Member, *Result.Context)
> +              << (Parents.size() > 1 ? "es" : "") << ParentsStr;
> +
> +  // Propose a fix if there's only one parent class...
> +  if (Parents.size() == 1 &&
> +      // ...unless parent class is templated
> +      !isa<ClassTemplateSpecializationDecl>(Parents.front()))
> +    Diag << FixItHint::CreateReplacement(
> +        Member->getQualifierLoc().getSourceRange(),
> +        getNameAsString(Parents.front()) + "::");
> +}
> +
> +} // namespace bugprone
> +} // namespace tidy
> +} // namespace clang
> 
> Propchange: clang-tools-extra/trunk/clang-tidy/bugprone/ParentVirtualCallCheck.cpp
> ------------------------------------------------------------------------------
>      svn:executable = *
> 
> Added: clang-tools-extra/trunk/clang-tidy/bugprone/ParentVirtualCallCheck.h
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/ParentVirtualCallCheck.h?rev=329448&view=auto
> ==============================================================================
> --- clang-tools-extra/trunk/clang-tidy/bugprone/ParentVirtualCallCheck.h (added)
> +++ clang-tools-extra/trunk/clang-tidy/bugprone/ParentVirtualCallCheck.h Fri Apr  6 13:02:50 2018
> @@ -0,0 +1,35 @@
> +//===--- ParentVirtualCallCheck.h - clang-tidy-------------------*- C++ -*-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_PARENTVIRTUALCALLCHECK_H
> +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_PARENTVIRTUALCALLCHECK_H
> +
> +#include "../ClangTidy.h"
> +
> +namespace clang {
> +namespace tidy {
> +namespace bugprone {
> +
> +/// Finds calls to grand..-parent virtual methods instead of parent's.
> +///
> +/// For the user-facing documentation see:
> +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-parent-virtual-call.html
> +class ParentVirtualCallCheck : public ClangTidyCheck {
> +public:
> +  ParentVirtualCallCheck(StringRef Name, ClangTidyContext *Context)
> +      : ClangTidyCheck(Name, Context) {}
> +  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
> +  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
> +};
> +
> +} // namespace bugprone
> +} // namespace tidy
> +} // namespace clang
> +
> +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_PARENTVIRTUALCALLCHECK_H
> 
> Propchange: clang-tools-extra/trunk/clang-tidy/bugprone/ParentVirtualCallCheck.h
> ------------------------------------------------------------------------------
>      svn:executable = *
> 
> Modified: clang-tools-extra/trunk/docs/ReleaseNotes.rst
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/ReleaseNotes.rst?rev=329448&r1=329447&r2=329448&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/docs/ReleaseNotes.rst (original)
> +++ clang-tools-extra/trunk/docs/ReleaseNotes.rst Fri Apr  6 13:02:50 2018
> @@ -64,6 +64,12 @@ Improvements to clang-tidy
>   
>   - New module ``zircon`` for checks related to Fuchsia's Zircon kernel.
>   
> +- New :doc:`bugprone-parent-virtual-call
> +  <clang-tidy/checks/bugprone-parent-virtual-call>` check
> +
> +  Detects and fixes calls to grand-...parent virtual methods instead of calls
> +  to overridden parent's virtual methods.
> +
>   - New :doc:`bugprone-throw-keyword-missing
>     <clang-tidy/checks/bugprone-throw-keyword-missing>` check
>   
> 
> Added: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-parent-virtual-call.rst
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-parent-virtual-call.rst?rev=329448&view=auto
> ==============================================================================
> --- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-parent-virtual-call.rst (added)
> +++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-parent-virtual-call.rst Fri Apr  6 13:02:50 2018
> @@ -0,0 +1,23 @@
> +.. title:: clang-tidy - bugprone-parent-virtual-call
> +
> +bugprone-parent-virtual-call
> +============================
> +
> +Detects and fixes calls to grand-...parent virtual methods instead of calls
> +to overridden parent's virtual methods.
> +
> +.. code-block:: c++
> +
> +  class A {
> +    int virtual foo() {...}
> +  };
> +
> +  class B: public A {
> +    int foo() override {...}
> +  };
> +
> +  class C: public B {
> +    int foo() override { A::foo(); }
> +  //                     ^^^^^^^^
> +  // warning: qualified name A::foo refers to a member overridden in subclass; did you mean 'B'?  [bugprone-parent-virtual-call]
> +  };
> 
> Propchange: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-parent-virtual-call.rst
> ------------------------------------------------------------------------------
>      svn:executable = *
> 
> Modified: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst?rev=329448&r1=329447&r2=329448&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst (original)
> +++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst Fri Apr  6 13:02:50 2018
> @@ -36,6 +36,7 @@ Clang-Tidy Checks
>      bugprone-misplaced-widening-cast
>      bugprone-move-forwarding-reference
>      bugprone-multiple-statement-macro
> +   bugprone-parent-virtual-call
>      bugprone-sizeof-container
>      bugprone-sizeof-expression
>      bugprone-string-constructor
> 
> Added: clang-tools-extra/trunk/test/clang-tidy/bugprone-parent-virtual-call.cpp
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/bugprone-parent-virtual-call.cpp?rev=329448&view=auto
> ==============================================================================
> --- clang-tools-extra/trunk/test/clang-tidy/bugprone-parent-virtual-call.cpp (added)
> +++ clang-tools-extra/trunk/test/clang-tidy/bugprone-parent-virtual-call.cpp Fri Apr  6 13:02:50 2018
> @@ -0,0 +1,179 @@
> +// RUN: %check_clang_tidy %s bugprone-parent-virtual-call %t
> +
> +extern int foo();
> +
> +class A {
> +public:
> +  A() = default;
> +  virtual ~A() = default;
> +
> +  virtual int virt_1() { return foo() + 1; }
> +  virtual int virt_2() { return foo() + 2; }
> +
> +  int non_virt() { return foo() + 3; }
> +  static int stat() { return foo() + 4; }
> +};
> +
> +class B : public A {
> +public:
> +  B() = default;
> +
> +  // Nothing to fix: calls to direct parent.
> +  int virt_1() override { return A::virt_1() + 3; }
> +  int virt_2() override { return A::virt_2() + 4; }
> +};
> +
> +class C : public B {
> +public:
> +  int virt_1() override { return A::virt_1() + B::virt_1(); }
> +  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' refers to a member overridden in subclass; did you mean 'B'? [bugprone-parent-virtual-call]
> +  // CHECK-FIXES:  int virt_1() override { return B::virt_1() + B::virt_1(); }
> +  int virt_2() override { return A::virt_1() + B::virt_1(); }
> +  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'B'? {{.*}}
> +  // CHECK-FIXES:  int virt_2() override { return B::virt_1() + B::virt_1(); }
> +
> +  // Test that non-virtual and static methods are not affected by this cherker.
> +  int method_c() { return A::stat() + A::non_virt(); }
> +};
> +
> +// Check aliased type names
> +using A1 = A;
> +typedef A A2;
> +#define A3 A
> +
> +class C2 : public B {
> +public:
> +  int virt_1() override { return A1::virt_1() + A2::virt_1() + A3::virt_1(); }
> +  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A1::virt_1' {{.*}}; did you mean 'B'? {{.*}}
> +  // CHECK-MESSAGES: :[[@LINE-2]]:49: warning: qualified name 'A2::virt_1' {{.*}}; did you mean 'B'? {{.*}}
> +  // CHECK-MESSAGES: :[[@LINE-3]]:64: warning: qualified name 'A3::virt_1' {{.*}}; did you mean 'B'? {{.*}}
> +  // CHECK-FIXES:  int virt_1() override { return B::virt_1() + B::virt_1() + B::virt_1(); }
> +};
> +
> +// Test that the check affects grand-grand..-parent calls too.
> +class D : public C {
> +public:
> +  int virt_1() override { return A::virt_1() + B::virt_1() + D::virt_1(); }
> +  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'C'? {{.*}}
> +  // CHECK-MESSAGES: :[[@LINE-2]]:48: warning: qualified name 'B::virt_1' {{.*}}; did you mean 'C'? {{.*}}
> +  // CHECK-FIXES:  int virt_1() override { return C::virt_1() + C::virt_1() + D::virt_1(); }
> +  int virt_2() override { return A::virt_1() + B::virt_1() + D::virt_1(); }
> +  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'C'? {{.*}}
> +  // CHECK-MESSAGES: :[[@LINE-2]]:48: warning: qualified name 'B::virt_1' {{.*}}; did you mean 'C'? {{.*}}
> +  // CHECK-FIXES:  int virt_2() override { return C::virt_1() + C::virt_1() + D::virt_1(); }
> +};
> +
> +// Test classes in namespaces.
> +namespace {
> +class BN : public A {
> +public:
> +  int virt_1() override { return A::virt_1() + 3; }
> +  int virt_2() override { return A::virt_2() + 4; }
> +};
> +} // namespace
> +
> +namespace N1 {
> +class A {
> +public:
> +  A() = default;
> +  virtual int virt_1() { return foo() + 1; }
> +  virtual int virt_2() { return foo() + 2; }
> +};
> +} // namespace N1
> +
> +namespace N2 {
> +class BN : public N1::A {
> +public:
> +  int virt_1() override { return A::virt_1() + 3; }
> +  int virt_2() override { return A::virt_2() + 4; }
> +};
> +} // namespace N2
> +
> +class CN : public BN {
> +public:
> +  int virt_1() override { return A::virt_1() + BN::virt_1(); }
> +  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'BN'? {{.*}}
> +  // CHECK-FIXES:  int virt_1() override { return BN::virt_1() + BN::virt_1(); }
> +  int virt_2() override { return A::virt_1() + BN::virt_1(); }
> +  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'BN'? {{.*}}
> +  // CHECK-FIXES:  int virt_2() override { return BN::virt_1() + BN::virt_1(); }
> +};
> +
> +class CNN : public N2::BN {
> +public:
> +  int virt_1() override { return N1::A::virt_1() + N2::BN::virt_1(); }
> +  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'N1::A::virt_1' {{.*}}; did you mean 'N2::BN'? {{.*}}
> +  // CHECK-FIXES:  int virt_1() override { return N2::BN::virt_1() + N2::BN::virt_1(); }
> +  int virt_2() override { return N1::A::virt_1() + N2::BN::virt_1(); }
> +  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'N1::A::virt_1' {{.*}}; did you mean 'N2::BN'? {{.*}}
> +  // CHECK-FIXES:  int virt_2() override { return N2::BN::virt_1() + N2::BN::virt_1(); }
> +};
> +
> +// Test multiple inheritance fixes
> +class AA {
> +public:
> +  AA() = default;
> +  virtual ~AA() = default;
> +
> +  virtual int virt_1() { return foo() + 1; }
> +  virtual int virt_2() { return foo() + 2; }
> +
> +  int non_virt() { return foo() + 3; }
> +  static int stat() { return foo() + 4; }
> +};
> +
> +class BB_1 : virtual public AA {
> +public:
> +  BB_1() = default;
> +
> +  // Nothing to fix: calls to parent.
> +  int virt_1() override { return AA::virt_1() + 3; }
> +  int virt_2() override { return AA::virt_2() + 4; }
> +};
> +
> +class BB_2 : virtual public AA {
> +public:
> +  BB_2() = default;
> +  int virt_1() override { return AA::virt_1() + 3; }
> +};
> +
> +class CC : public BB_1, public BB_2 {
> +public:
> +  int virt_1() override { return AA::virt_1() + 3; }
> +  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'AA::virt_1' refers to a member overridden in subclasses; did you mean 'BB_1' or 'BB_2'? {{.*}}
> +  // No fix available due to multiple choice of parent class.
> +};
> +
> +// Test that virtual method is not diagnosed as not overridden in parent.
> +class BI : public A {
> +public:
> +  BI() = default;
> +};
> +
> +class CI : BI {
> +  int virt_1() override { return A::virt_1(); }
> +};
> +
> +// Test templated classes.
> +template <class F> class BF : public A {
> +public:
> +  int virt_1() override { return A::virt_1() + 3; }
> +};
> +
> +// Test templated parent class.
> +class CF : public BF<int> {
> +public:
> +  int virt_1() override { return A::virt_1(); }
> +  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'BF'? {{.*}}
> +};
> +
> +// Test both templated class and its parent class.
> +template <class F> class DF : public BF<F> {
> +public:
> +  DF() = default;
> +  int virt_1() override { return A::virt_1(); }
> +  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: qualified name 'A::virt_1' {{.*}}; did you mean 'BF'? {{.*}}
> +};
> +
> +// Just to instantiate DF<F>.
> +int bar() { return (new DF<int>())->virt_1(); }
> 
> Propchange: clang-tools-extra/trunk/test/clang-tidy/bugprone-parent-virtual-call.cpp
> ------------------------------------------------------------------------------
>      svn:executable = *
> 
> 
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
> 


More information about the cfe-commits mailing list