[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

llvm-svn: 323011
This commit is contained in:
Julie Hockett 2018-01-19 23:59:59 +00:00
parent 517366c7e0
commit 587deb4557
8 changed files with 360 additions and 0 deletions

View File

@ -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

View File

@ -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>(

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {};