[clang-tools-extra] r323011 - [clang-tidy] Adding Fuchsia checker for multiple inheritance

Julie Hockett via cfe-commits cfe-commits at lists.llvm.org
Fri Jan 19 16:00:00 PST 2018


Author: juliehockett
Date: Fri Jan 19 15:59:59 2018
New Revision: 323011

URL: http://llvm.org/viewvc/llvm-project?rev=323011&view=rev
Log:
[clang-tidy] Adding Fuchsia checker for multiple inheritance

Adds a check to the Fuchsia module to warn when a class
inherits from multiple classes that are not pure virtual.

See https://fuchsia.googlesource.com/zircon/+/master/docs/cxx.md
for reference.

Differential Revision: https://reviews.llvm.org/D40580

Added:
    clang-tools-extra/trunk/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp
    clang-tools-extra/trunk/clang-tidy/fuchsia/MultipleInheritanceCheck.h
    clang-tools-extra/trunk/docs/clang-tidy/checks/fuchsia-multiple-inheritance.rst
    clang-tools-extra/trunk/test/clang-tidy/fuchsia-multiple-inheritance.cpp
Modified:
    clang-tools-extra/trunk/clang-tidy/fuchsia/CMakeLists.txt
    clang-tools-extra/trunk/clang-tidy/fuchsia/FuchsiaTidyModule.cpp
    clang-tools-extra/trunk/docs/ReleaseNotes.rst
    clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst

Modified: clang-tools-extra/trunk/clang-tidy/fuchsia/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/fuchsia/CMakeLists.txt?rev=323011&r1=323010&r2=323011&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/fuchsia/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clang-tidy/fuchsia/CMakeLists.txt Fri Jan 19 15:59:59 2018
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS support)
 add_clang_library(clangTidyFuchsiaModule
   DefaultArgumentsCheck.cpp
   FuchsiaTidyModule.cpp
+  MultipleInheritanceCheck.cpp
   OverloadedOperatorCheck.cpp
   StaticallyConstructedObjectsCheck.cpp
   TrailingReturnCheck.cpp

Modified: clang-tools-extra/trunk/clang-tidy/fuchsia/FuchsiaTidyModule.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/fuchsia/FuchsiaTidyModule.cpp?rev=323011&r1=323010&r2=323011&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/fuchsia/FuchsiaTidyModule.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/fuchsia/FuchsiaTidyModule.cpp Fri Jan 19 15:59:59 2018
@@ -11,6 +11,7 @@
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
 #include "DefaultArgumentsCheck.h"
+#include "MultipleInheritanceCheck.h"
 #include "OverloadedOperatorCheck.h"
 #include "StaticallyConstructedObjectsCheck.h"
 #include "TrailingReturnCheck.h"
@@ -28,6 +29,8 @@ public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
     CheckFactories.registerCheck<DefaultArgumentsCheck>(
         "fuchsia-default-arguments");
+    CheckFactories.registerCheck<MultipleInheritanceCheck>(
+        "fuchsia-multiple-inheritance");
     CheckFactories.registerCheck<OverloadedOperatorCheck>(
         "fuchsia-overloaded-operator");
     CheckFactories.registerCheck<StaticallyConstructedObjectsCheck>(

Added: clang-tools-extra/trunk/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp?rev=323011&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp (added)
+++ clang-tools-extra/trunk/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp Fri Jan 19 15:59:59 2018
@@ -0,0 +1,125 @@
+//===--- MultipleInheritanceCheck.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 "MultipleInheritanceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace fuchsia {
+  
+AST_MATCHER(CXXRecordDecl, hasBases) {
+  if (Node.hasDefinition())
+    return Node.getNumBases() > 0;
+  return false;
+}
+
+// Adds a node (by name) to the interface map, if it was not present in the map
+// previously.
+void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl *Node,
+                                                     bool isInterface) {
+  StringRef Name = Node->getIdentifier()->getName();
+  InterfaceMap.insert(std::make_pair(Name, isInterface));
+}
+
+// Returns "true" if the boolean "isInterface" has been set to the
+// interface status of the current Node. Return "false" if the
+// interface status for the current node is not yet known.
+bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node,
+                                                  bool &isInterface) const {
+  StringRef Name = Node->getIdentifier()->getName();
+  llvm::StringMapConstIterator<bool> Pair = InterfaceMap.find(Name);
+  if (Pair == InterfaceMap.end())
+    return false;
+  isInterface = Pair->second;
+  return true;
+}
+
+bool MultipleInheritanceCheck::isCurrentClassInterface(
+    const CXXRecordDecl *Node) const {
+  // Interfaces should have no fields.
+  if (!Node->field_empty()) return false;
+
+  // Interfaces should have exclusively pure methods.
+  return llvm::none_of(Node->methods(), [](const CXXMethodDecl *M) {
+    return M->isUserProvided() && !M->isPure() && !M->isStatic();
+  });
+}
+
+bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) {
+  // Short circuit the lookup if we have analyzed this record before.
+  bool PreviousIsInterfaceResult;
+  if (getInterfaceStatus(Node, PreviousIsInterfaceResult))
+    return PreviousIsInterfaceResult;
+
+  // To be an interface, all base classes must be interfaces as well.
+  for (const auto &I : Node->bases()) {
+    if (I.isVirtual()) continue;  
+    const auto *Ty = I.getType()->getAs<RecordType>();
+    assert(Ty && "RecordType of base class is unknown");
+    const RecordDecl *D = Ty->getDecl()->getDefinition();
+    if (!D) continue;
+    const auto *Base = cast<CXXRecordDecl>(D);
+    if (!isInterface(Base)) {
+      addNodeToInterfaceMap(Node, false);
+      return false;
+    }
+  }
+
+  bool CurrentClassIsInterface = isCurrentClassInterface(Node);
+  addNodeToInterfaceMap(Node, CurrentClassIsInterface);
+  return CurrentClassIsInterface;
+}
+
+void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) {
+  // Requires C++.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Match declarations which have bases.
+  Finder->addMatcher(cxxRecordDecl(hasBases()).bind("decl"), this);
+}
+
+void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl")) {
+    // Check against map to see if if the class inherits from multiple
+    // concrete classes
+    unsigned NumConcrete = 0;
+    for (const auto &I : D->bases()) {
+      if (I.isVirtual()) continue;
+      const auto *Ty = I.getType()->getAs<RecordType>();
+      assert(Ty && "RecordType of base class is unknown");
+      const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
+      if (!isInterface(Base)) NumConcrete++;
+    }
+    
+    // Check virtual bases to see if there is more than one concrete 
+    // non-virtual base.
+    for (const auto &V : D->vbases()) {
+      const auto *Ty = V.getType()->getAs<RecordType>();
+      assert(Ty && "RecordType of base class is unknown");
+      const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
+      if (!isInterface(Base)) NumConcrete++;
+    }
+
+    if (NumConcrete > 1) {
+      diag(D->getLocStart(),
+           "inheriting mulitple classes that aren't "
+           "pure virtual is discouraged");
+    }
+  }
+}
+
+}  // namespace fuchsia
+}  // namespace tidy
+}  // namespace clang

Added: clang-tools-extra/trunk/clang-tidy/fuchsia/MultipleInheritanceCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/fuchsia/MultipleInheritanceCheck.h?rev=323011&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/fuchsia/MultipleInheritanceCheck.h (added)
+++ clang-tools-extra/trunk/clang-tidy/fuchsia/MultipleInheritanceCheck.h Fri Jan 19 15:59:59 2018
@@ -0,0 +1,48 @@
+//===--- MultipleInheritanceCheck.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_FUCHSIA_MULTIPLE_INHERITANCE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FUCHSIA_MULTIPLE_INHERITANCE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace fuchsia {
+
+/// Mulitple implementation inheritance is discouraged.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/fuchsia-multiple-inheritance.html
+class MultipleInheritanceCheck : public ClangTidyCheck {
+public:
+  MultipleInheritanceCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+  void onEndOfTranslationUnit() override { InterfaceMap.clear(); }
+
+private:
+  void addNodeToInterfaceMap(const CXXRecordDecl *Node, bool isInterface);
+  bool getInterfaceStatus(const CXXRecordDecl *Node, bool &isInterface) const;
+  bool isCurrentClassInterface(const CXXRecordDecl *Node) const;
+  bool isInterface(const CXXRecordDecl *Node);
+
+  // Contains the identity of each named CXXRecord as an interface.  This is
+  // used to memoize lookup speeds and improve performance from O(N^2) to O(N),
+  // where N is the number of classes.
+  llvm::StringMap<bool> InterfaceMap;
+};
+
+} // namespace fuchsia
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FUCHSIA_MULTIPLE_INHERITANCE_H

Modified: clang-tools-extra/trunk/docs/ReleaseNotes.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/ReleaseNotes.rst?rev=323011&r1=323010&r2=323011&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/ReleaseNotes.rst (original)
+++ clang-tools-extra/trunk/docs/ReleaseNotes.rst Fri Jan 19 15:59:59 2018
@@ -64,6 +64,11 @@ Improvements to clang-tidy
   with looping constructs. Every backward jump is rejected. Forward jumps are
   only allowed in nested loops.
 
+- New `fuchsia-multiple-inheritance
+  <http://clang.llvm.org/extra/clang-tidy/checks/fuchsia-multiple-inheritance.html>`_ check
+
+  Warns if a class inherits from multiple classes that are not pure virtual.
+
 - New `fuchsia-statically-constructed-objects
   <http://clang.llvm.org/extra/clang-tidy/checks/fuchsia-statically-constructed-objects.html>`_ check
 

Added: clang-tools-extra/trunk/docs/clang-tidy/checks/fuchsia-multiple-inheritance.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/fuchsia-multiple-inheritance.rst?rev=323011&view=auto
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/fuchsia-multiple-inheritance.rst (added)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/fuchsia-multiple-inheritance.rst Fri Jan 19 15:59:59 2018
@@ -0,0 +1,46 @@
+.. title:: clang-tidy - fuchsia-multiple-inheritance
+
+fuchsia-multiple-inheritance
+============================
+
+Warns if a class inherits from multiple classes that are not pure virtual.
+
+For example, declaring a class that inherits from multiple concrete classes is
+disallowed:
+
+.. code-block:: c++
+
+  class Base_A {
+  public:
+    virtual int foo() { return 0; }
+  };
+
+  class Base_B {
+  public:
+    virtual int bar() { return 0; }
+  };
+
+  // Warning
+  class Bad_Child1 : public Base_A, Base_B {};
+
+A class that inherits from a pure virtual is allowed:
+
+.. code-block:: c++
+
+  class Interface_A {
+  public:
+    virtual int foo() = 0;
+  };
+
+  class Interface_B {
+  public:
+    virtual int bar() = 0;
+  };
+
+  // No warning
+  class Good_Child1 : public Interface_A, Interface_B {
+    virtual int foo() override { return 0; }
+    virtual int bar() override { return 0; }
+  };
+
+See the features disallowed in Fuchsia at https://fuchsia.googlesource.com/zircon/+/master/docs/cxx.md

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=323011&r1=323010&r2=323011&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst (original)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst Fri Jan 19 15:59:59 2018
@@ -70,6 +70,7 @@ Clang-Tidy Checks
    cppcoreguidelines-slicing
    cppcoreguidelines-special-member-functions
    fuchsia-default-arguments
+   fuchsia-multiple-inheritance
    fuchsia-overloaded-operator
    fuchsia-statically-constructed-objects
    fuchsia-trailing-return

Added: clang-tools-extra/trunk/test/clang-tidy/fuchsia-multiple-inheritance.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/fuchsia-multiple-inheritance.cpp?rev=323011&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/fuchsia-multiple-inheritance.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/fuchsia-multiple-inheritance.cpp Fri Jan 19 15:59:59 2018
@@ -0,0 +1,131 @@
+// RUN: %check_clang_tidy %s fuchsia-multiple-inheritance %t
+
+class Base_A {
+public:
+  virtual int foo() { return 0; }
+};
+
+class Base_B {
+public:
+  virtual int bar() { return 0; }
+};
+
+class Base_A_child : public Base_A {
+public:
+  virtual int baz() { return 0; }
+};
+
+class Interface_A {
+public:
+  virtual int foo() = 0;
+};
+
+class Interface_B {
+public:
+  virtual int bar() = 0;
+};
+
+class Interface_C {
+public:
+  virtual int blat() = 0;
+};
+
+class Interface_A_with_member {
+public:
+  virtual int foo() = 0;
+  int val = 0;
+};
+
+class Interface_with_A_Parent : public Base_A {
+public:
+  virtual int baz() = 0;
+};
+
+// Inherits from multiple concrete classes.
+// CHECK-MESSAGES: [[@LINE+2]]:1: warning: inheriting mulitple classes that aren't pure virtual is discouraged [fuchsia-multiple-inheritance]
+// CHECK-NEXT: class Bad_Child1 : public Base_A, Base_B {};
+class Bad_Child1 : public Base_A, Base_B {};
+
+// CHECK-MESSAGES: [[@LINE+1]]:1: warning: inheriting mulitple classes that aren't pure virtual is discouraged [fuchsia-multiple-inheritance]
+class Bad_Child2 : public Base_A, Interface_A_with_member {
+  virtual int foo() override { return 0; }
+};
+
+// CHECK-MESSAGES: [[@LINE+2]]:1: warning: inheriting mulitple classes that aren't pure virtual is discouraged [fuchsia-multiple-inheritance]
+// CHECK-NEXT: class Bad_Child3 : public Interface_with_A_Parent, Base_B {
+class Bad_Child3 : public Interface_with_A_Parent, Base_B {
+  virtual int baz() override { return 0; }
+};
+
+// Easy cases of single inheritance
+class Simple_Child1 : public Base_A {};
+class Simple_Child2 : public Interface_A {
+  virtual int foo() override { return 0; }
+};
+
+// Valid uses of multiple inheritance
+class Good_Child1 : public Interface_A, Interface_B {
+  virtual int foo() override { return 0; }
+  virtual int bar() override { return 0; }
+};
+
+class Good_Child2 : public Base_A, Interface_B {
+  virtual int bar() override { return 0; }
+};
+
+class Good_Child3 : public Base_A_child, Interface_C, Interface_B {
+  virtual int bar() override { return 0; }
+  virtual int blat() override { return 0; }
+};
+
+struct B1 { int x; };
+struct B2 { int x;};
+// CHECK-MESSAGES: [[@LINE+2]]:1: warning: inheriting mulitple classes that aren't pure virtual is discouraged [fuchsia-multiple-inheritance]
+// CHECK-NEXT: struct D : B1, B2 {};
+struct D1 : B1, B2 {};
+
+struct Base1 { virtual void foo() = 0; };
+struct V1 : virtual Base1 {};
+struct V2 : virtual Base1 {};
+struct D2 : V1, V2 {};
+
+struct Base2 { virtual void foo(); };
+struct V3 : virtual Base2 {};
+struct V4 : virtual Base2 {};
+struct D3 : V3, V4 {};
+
+struct Base3 {};
+struct V5 : virtual Base3 { virtual void f(); };
+struct V6 : virtual Base3 { virtual void g(); };
+// CHECK-MESSAGES: [[@LINE+2]]:1: warning: inheriting mulitple classes that aren't pure virtual is discouraged [fuchsia-multiple-inheritance]
+// CHECK-NEXT: struct D4 : V5, V6 {};
+struct D4 : V5, V6 {};
+
+struct Base4 {};
+struct V7 : virtual Base4 { virtual void f() = 0; };
+struct V8 : virtual Base4 { virtual void g() = 0; };
+struct D5 : V7, V8 {};
+
+struct Base5 { virtual void f() = 0; };
+struct V9 : virtual Base5 { virtual void f(); };
+struct V10 : virtual Base5 { virtual void g() = 0; };
+struct D6 : V9, V10 {};
+
+struct Base6 { virtual void f(); };
+struct Base7 { virtual void g(); };
+struct V15 : virtual Base6 { virtual void f() = 0; };
+struct V16 : virtual Base7 { virtual void g() = 0; };
+// CHECK-MESSAGES: [[@LINE+2]]:1: warning: inheriting mulitple classes that aren't pure virtual is discouraged [fuchsia-multiple-inheritance]
+// CHECK-NEXT: struct D9 : V15, V16 {};
+struct D9 : V15, V16 {};
+
+struct Static_Base { static void foo(); };
+struct V11 : virtual Static_Base {};
+struct V12 : virtual Static_Base {};
+struct D7 : V11, V12 {};
+
+struct Static_Base_2 {};
+struct V13 : virtual Static_Base_2 { static void f(); };
+struct V14 : virtual Static_Base_2 { static void g(); };
+struct D8 : V13, V14 {};
+




More information about the cfe-commits mailing list