[clang-tools-extra] r257599 - Support virtual-near-miss check.
Alexander Kornienko via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 13 06:16:35 PST 2016
Author: alexfh
Date: Wed Jan 13 08:16:35 2016
New Revision: 257599
URL: http://llvm.org/viewvc/llvm-project?rev=257599&view=rev
Log:
Support virtual-near-miss check.
Summary: Virtual function override near miss detection. Function complete. Test complete. Do not conduct Fix for now.
Reviewers: alexfh
Subscribers: cfe-commits
Patch by Cong Liu!
Differential Revision: http://reviews.llvm.org/D15823
Added:
clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.cpp
clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.h
clang-tools-extra/trunk/docs/clang-tidy/checks/misc-virtual-near-miss.rst
clang-tools-extra/trunk/test/clang-tidy/misc-virtual-near-miss.cpp
Modified:
clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt
clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp
clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
Modified: clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt?rev=257599&r1=257598&r2=257599&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt Wed Jan 13 08:16:35 2016
@@ -26,6 +26,7 @@ add_clang_library(clangTidyMiscModule
UnusedParametersCheck.cpp
UnusedRAIICheck.cpp
UniqueptrResetReleaseCheck.cpp
+ VirtualNearMissCheck.cpp
LINK_LIBS
clangAST
Modified: clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp?rev=257599&r1=257598&r2=257599&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp Wed Jan 13 08:16:35 2016
@@ -34,6 +34,7 @@
#include "UnusedAliasDeclsCheck.h"
#include "UnusedParametersCheck.h"
#include "UnusedRAIICheck.h"
+#include "VirtualNearMissCheck.h"
namespace clang {
namespace tidy {
@@ -87,6 +88,8 @@ public:
CheckFactories.registerCheck<UnusedParametersCheck>(
"misc-unused-parameters");
CheckFactories.registerCheck<UnusedRAIICheck>("misc-unused-raii");
+ CheckFactories.registerCheck<VirtualNearMissCheck>(
+ "misc-virtual-near-miss");
}
};
Added: clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.cpp?rev=257599&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.cpp (added)
+++ clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.cpp Wed Jan 13 08:16:35 2016
@@ -0,0 +1,266 @@
+//===--- VirtualNearMissCheck.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 "VirtualNearMissCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds out if the given method overrides some method.
+static bool isOverrideMethod(const CXXMethodDecl *MD) {
+ return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
+}
+
+/// Checks whether the return types are covariant, according to
+/// C++[class.virtual]p7.
+///
+/// Similar with clang::Sema::CheckOverridingFunctionReturnType.
+/// \returns true if the return types of BaseMD and DerivedMD are covariant.
+static bool checkOverridingFunctionReturnType(const ASTContext *Context,
+ const CXXMethodDecl *BaseMD,
+ const CXXMethodDecl *DerivedMD) {
+ QualType BaseReturnTy =
+ BaseMD->getType()->getAs<FunctionType>()->getReturnType();
+ QualType DerivedReturnTy =
+ DerivedMD->getType()->getAs<FunctionType>()->getReturnType();
+
+ if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
+ return false;
+
+ // Check if return types are identical.
+ if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
+ return true;
+
+ /// Check if the return types are covariant.
+ /// BTy is the class type in return type of BaseMD. For example,
+ /// B* Base::md()
+ /// While BRD is the declaration of B.
+ QualType BTy, DTy;
+ const CXXRecordDecl *BRD, *DRD;
+
+ // Both types must be pointers or references to classes.
+ if (const auto *DerivedPT = DerivedReturnTy->getAs<PointerType>()) {
+ if (const auto *BasePT = BaseReturnTy->getAs<PointerType>()) {
+ DTy = DerivedPT->getPointeeType();
+ BTy = BasePT->getPointeeType();
+ }
+ } else if (const auto *DerivedRT = DerivedReturnTy->getAs<ReferenceType>()) {
+ if (const auto *BaseRT = BaseReturnTy->getAs<ReferenceType>()) {
+ DTy = DerivedRT->getPointeeType();
+ BTy = BaseRT->getPointeeType();
+ }
+ }
+
+ // The return types aren't either both pointers or references to a class type.
+ if (DTy.isNull())
+ return false;
+
+ DRD = DTy->getAsCXXRecordDecl();
+ BRD = BTy->getAsCXXRecordDecl();
+ if (DRD == nullptr || BRD == nullptr)
+ return false;
+
+ if (DRD == BRD)
+ return true;
+
+ if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
+ // Begin checking whether the conversion from D to B is valid.
+ CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
+ /*DetectVirtual=*/false);
+
+ // Check whether D is derived from B, and fill in a CXXBasePaths object.
+ if (!DRD->isDerivedFrom(BRD, Paths))
+ return false;
+
+ // Check ambiguity.
+ if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
+ return false;
+
+ // Check accessibility.
+ // FIXME: We currently only support checking if B is accessible base class
+ // of D, or D is the same class which DerivedMD is in.
+ bool IsItself = DRD == DerivedMD->getParent();
+ bool HasPublicAccess = false;
+ for (const auto &Path : Paths) {
+ if (Path.Access == AS_public)
+ HasPublicAccess = true;
+ }
+ if (!HasPublicAccess && !IsItself)
+ return false;
+ // End checking conversion from D to B.
+ }
+
+ // Both pointers or references should have the same cv-qualification.
+ if (DerivedReturnTy.getLocalCVRQualifiers() !=
+ BaseReturnTy.getLocalCVRQualifiers())
+ return false;
+
+ // The class type D should have the same cv-qualification as or less
+ // cv-qualification than the class type B.
+ if (DTy.isMoreQualifiedThan(BTy))
+ return false;
+
+ return true;
+}
+
+/// \returns true if the param types are the same.
+static bool checkParamTypes(const CXXMethodDecl *BaseMD,
+ const CXXMethodDecl *DerivedMD) {
+ unsigned NumParamA = BaseMD->getNumParams();
+ unsigned NumParamB = DerivedMD->getNumParams();
+ if (NumParamA != NumParamB)
+ return false;
+
+ for (unsigned I = 0; I < NumParamA; I++) {
+ if (BaseMD->getParamDecl(I)->getType() !=
+ DerivedMD->getParamDecl(I)->getType())
+ return false;
+ }
+ return true;
+}
+
+/// \returns true if derived method can override base method except for the
+/// name.
+static bool checkOverrideWithoutName(const ASTContext *Context,
+ const CXXMethodDecl *BaseMD,
+ const CXXMethodDecl *DerivedMD) {
+ if (BaseMD->getTypeQualifiers() != DerivedMD->getTypeQualifiers())
+ return false;
+
+ if (BaseMD->isStatic() != DerivedMD->isStatic())
+ return false;
+
+ if (BaseMD->getAccess() != DerivedMD->getAccess())
+ return false;
+
+ if (BaseMD->getType() == DerivedMD->getType())
+ return true;
+
+ // Now the function types are not identical. Then check if the return types
+ // are covariant and if the param types are the same.
+ if (!checkOverridingFunctionReturnType(Context, BaseMD, DerivedMD))
+ return false;
+ return checkParamTypes(BaseMD, DerivedMD);
+}
+
+/// Check whether BaseMD overrides DerivedMD.
+///
+/// Prerequisite: the class which BaseMD is in should be a base class of that
+/// DerivedMD is in.
+static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
+ const CXXMethodDecl *DerivedMD) {
+ if (BaseMD->getNameAsString() != DerivedMD->getNameAsString())
+ return false;
+
+ if (!checkParamTypes(BaseMD, DerivedMD))
+ return false;
+
+ return true;
+}
+
+/// Generate unique ID for given MethodDecl.
+///
+/// The Id is used as key for 'PossibleMap'.
+/// Typical Id: "Base::func void (void)"
+static std::string generateMethodId(const CXXMethodDecl *MD) {
+ return MD->getQualifiedNameAsString() + " " + MD->getType().getAsString();
+}
+
+bool VirtualNearMissCheck::isPossibleToBeOverridden(
+ const CXXMethodDecl *BaseMD) {
+ std::string Id = generateMethodId(BaseMD);
+ auto Iter = PossibleMap.find(Id);
+ if (Iter != PossibleMap.end())
+ return Iter->second;
+
+ bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
+ BaseMD->isVirtual();
+ PossibleMap[Id] = IsPossible;
+ return IsPossible;
+}
+
+bool VirtualNearMissCheck::isOverriddenByDerivedClass(
+ const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) {
+ auto Key = std::make_pair(generateMethodId(BaseMD),
+ DerivedRD->getQualifiedNameAsString());
+ auto Iter = OverriddenMap.find(Key);
+ if (Iter != OverriddenMap.end())
+ return Iter->second;
+
+ bool IsOverridden = false;
+ for (const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
+ if (!isOverrideMethod(DerivedMD))
+ continue;
+
+ if (checkOverrideByDerivedMethod(BaseMD, DerivedMD)) {
+ IsOverridden = true;
+ break;
+ }
+ }
+ OverriddenMap[Key] = IsOverridden;
+ return IsOverridden;
+}
+
+void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(cxxMethodDecl(unless(anyOf(isOverride(), isImplicit(),
+ cxxConstructorDecl())))
+ .bind("method"),
+ this);
+}
+
+void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
+ assert(DerivedMD != nullptr);
+
+ if (DerivedMD->isStatic())
+ return;
+
+ const ASTContext *Context = Result.Context;
+
+ const auto *DerivedRD = DerivedMD->getParent();
+
+ for (const auto &BaseSpec : DerivedRD->bases()) {
+ if (const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
+ for (const auto *BaseMD : BaseRD->methods()) {
+ if (!isPossibleToBeOverridden(BaseMD))
+ continue;
+
+ if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
+ continue;
+
+ unsigned EditDistance =
+ BaseMD->getName().edit_distance(DerivedMD->getName());
+ if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
+ if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
+ // A "virtual near miss" is found.
+ diag(DerivedMD->getLocStart(),
+ "method '%0' has a similar name and the same signature as "
+ "virtual method '%1'; did you mean to override it?")
+ << DerivedMD->getQualifiedNameAsString()
+ << BaseMD->getQualifiedNameAsString();
+ }
+ }
+ }
+ }
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
Added: clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.h?rev=257599&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.h (added)
+++ clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.h Wed Jan 13 08:16:35 2016
@@ -0,0 +1,65 @@
+//===--- VirtualNearMissCheck.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_MISC_VIRTUAL_NEAR_MISS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_VIRTUAL_NEAR_MISS_H
+
+#include "../ClangTidy.h"
+#include <map>
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// \brief Checks for near miss of virtual methods.
+///
+/// For a method in a derived class, this check looks for virtual method with a
+/// very similar name and an identical signature defined in a base class.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-virtual-near-miss.html
+class VirtualNearMissCheck : public ClangTidyCheck {
+public:
+ VirtualNearMissCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ /// Check if the given method is possible to be overridden by some other
+ /// method.
+ ///
+ /// It should look up the PossibleMap or update it.
+ bool isPossibleToBeOverridden(const CXXMethodDecl *BaseMD);
+
+ /// Check if the given base method is overridden by some methods in the given
+ /// derived class.
+ ///
+ /// It should look up the OverriddenMap or update it.
+ bool isOverriddenByDerivedClass(const CXXMethodDecl *BaseMD,
+ const CXXRecordDecl *DerivedRD);
+
+ /// key: the unique ID of a method;
+ /// value: whether the method is possible to be overridden.
+ std::map<std::string, bool> PossibleMap;
+
+ /// key: <unique ID of base method, name of derived class>
+ /// value: whether the base method is overridden by some method in the derived
+ /// class.
+ std::map<std::pair<std::string, std::string>, bool> OverriddenMap;
+
+ const unsigned EditDistanceThreshold = 1;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_VIRTUAL_NEAR_MISS_H
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=257599&r1=257598&r2=257599&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst (original)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst Wed Jan 13 08:16:35 2016
@@ -65,6 +65,7 @@ Clang-Tidy Checks
misc-unused-alias-decls
misc-unused-parameters
misc-unused-raii
+ misc-virtual-near-miss
modernize-loop-convert
modernize-make-unique
modernize-pass-by-value
Added: clang-tools-extra/trunk/docs/clang-tidy/checks/misc-virtual-near-miss.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/misc-virtual-near-miss.rst?rev=257599&view=auto
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/misc-virtual-near-miss.rst (added)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/misc-virtual-near-miss.rst Wed Jan 13 08:16:35 2016
@@ -0,0 +1,17 @@
+misc-virtual-near-miss
+======================
+
+Warn if a function is a near miss (ie. the name is very similar and the function signiture is the same) to a virtual function from a base class.
+
+Example:
+
+.. code-block:: c++
+
+ struct Base {
+ virtual void func();
+ };
+
+ struct Derived : Base {
+ virtual funk();
+ // warning: Do you want to override 'func'?
+ };
Added: clang-tools-extra/trunk/test/clang-tidy/misc-virtual-near-miss.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/misc-virtual-near-miss.cpp?rev=257599&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/misc-virtual-near-miss.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/misc-virtual-near-miss.cpp Wed Jan 13 08:16:35 2016
@@ -0,0 +1,65 @@
+// RUN: %check_clang_tidy %s misc-virtual-near-miss %t
+
+struct Base {
+ virtual void func();
+ virtual void gunk();
+};
+
+struct Derived : Base {
+ // Should not warn "do you want to override 'gunk'?", because gunk is already
+ // overriden by this class.
+ virtual void funk();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::funk' has a similar name and the same signature as virtual method 'Base::func'; did you mean to override it? [misc-virtual-near-miss]
+
+ void func2();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::func2' has {{.*}} 'Base::func'
+
+ void func22(); // Should not warn.
+
+ void gunk(); // Should not warn: gunk is override.
+
+ void fun();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::fun' has {{.*}} 'Base::func'
+};
+
+class Father {
+public:
+ Father();
+ virtual void func();
+ virtual Father *create(int i);
+ virtual Base &&generate();
+};
+
+class Mother {
+public:
+ Mother();
+ static void method();
+ virtual int method(int argc, const char **argv);
+ virtual int method(int argc) const;
+};
+
+class Child : private Father, private Mother {
+public:
+ Child();
+
+ virtual void func2();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::func2' has {{.*}} 'Father::func'
+
+ int methoe(int x, char **strs); // Should not warn: parameter types don't match.
+
+ int methoe(int x); // Should not warn: method is not const.
+
+ void methof(int x, const char **strs); // Should not warn: return types don't match.
+
+ int methoh(int x, const char **strs);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::methoh' has {{.*}} 'Mother::method'
+
+ virtual Child *creat(int i);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::creat' has {{.*}} 'Father::create'
+
+ virtual Derived &&generat();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::generat' has {{.*}} 'Father::generate'
+
+private:
+ void funk(); // Should not warn: access qualifers don't match.
+};
More information about the cfe-commits
mailing list