Index: clang-tidy/misc/VirtualNearMissCheck.cpp =================================================================== --- clang-tidy/misc/VirtualNearMissCheck.cpp +++ clang-tidy/misc/VirtualNearMissCheck.cpp @@ -178,6 +178,50 @@ return false; } +/// Get declaration given the type of a class (including template class). +/// +/// This function is copied from the one defined in ASTMatchFinder.cpp to solve +/// the problem that QualType::getAsCXXRecordDecl does not work for template +/// class. +static CXXRecordDecl *getAsCXXRecordDecl(const Type *TypeNode) { + if (TypeNode->getAs() != nullptr || + TypeNode->getAs() != nullptr || + TypeNode->getAs() != nullptr) + // Dependent names and template TypeNode parameters will be matched when the + // template is instantiated. + return nullptr; + TemplateSpecializationType const *TemplateType = + TypeNode->getAs(); + if (!TemplateType) + return TypeNode->getAsCXXRecordDecl(); + + if (TemplateType->getTemplateName().isDependent()) + // Dependent template specializations will be matched when the template is + // instantiated. + return nullptr; + // For template specialization types which are specializing a template + // declaration which is an explicit or partial specialization of another + // template declaration, getAsCXXRecordDecl() returns the corresponding + // ClassTemplateSpecializationDecl. + // + // For template specialization types which are specializing a template + // declaration which is neither an explicit nor partial specialization of + // another template declaration, getAsCXXRecordDecl() returns NULL and we get + // the CXXRecordDecl of the templated declaration. + CXXRecordDecl *SpecializationDecl = TemplateType->getAsCXXRecordDecl(); + if (SpecializationDecl) + return SpecializationDecl; + + NamedDecl *Templated = + TemplateType->getTemplateName().getAsTemplateDecl()->getTemplatedDecl(); + if (CXXRecordDecl *TemplatedRecord = dyn_cast(Templated)) + return TemplatedRecord; + + TypeAliasDecl *AliasDecl = dyn_cast(Templated); + assert(AliasDecl); + return getAsCXXRecordDecl(AliasDecl->getUnderlyingType().getTypePtr()); +} + bool VirtualNearMissCheck::isPossibleToBeOverridden( const CXXMethodDecl *BaseMD) { auto Iter = PossibleMap.find(BaseMD); @@ -187,7 +231,8 @@ bool IsPossible = !BaseMD->isImplicit() && !isa(BaseMD) && !isa(BaseMD) && BaseMD->isVirtual() && !BaseMD->isOverloadedOperator() && - !isa(BaseMD); + !isa(BaseMD) && + !BaseMD->isTemplateInstantiation(); PossibleMap[BaseMD] = IsPossible; return IsPossible; } @@ -221,7 +266,8 @@ cxxMethodDecl( unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(), cxxDestructorDecl(), cxxConversionDecl(), isStatic(), - isOverloadedOperator()))) + isOverloadedOperator(), + clang::ast_matchers::isTemplateInstantiation()))) .bind("method"), this); } @@ -236,7 +282,8 @@ assert(DerivedRD); for (const auto &BaseSpec : DerivedRD->bases()) { - if (const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) { + if (const auto *BaseRD = + getAsCXXRecordDecl(BaseSpec.getType().getTypePtr())) { for (const auto *BaseMD : BaseRD->methods()) { if (!isPossibleToBeOverridden(BaseMD)) continue; @@ -249,11 +296,14 @@ if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) { if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) { // A "virtual near miss" is found. + auto Range = CharSourceRange::getTokenRange( + SourceRange(DerivedMD->getLocation())); 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(); + << BaseMD->getQualifiedNameAsString() + << FixItHint::CreateReplacement(Range, BaseMD->getName()); } } } Index: test/clang-tidy/misc-virtual-near-miss.cpp =================================================================== --- test/clang-tidy/misc-virtual-near-miss.cpp +++ test/clang-tidy/misc-virtual-near-miss.cpp @@ -16,9 +16,11 @@ // 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] + // CHECK-FIXES: virtual void func(); void func2(); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::func2' has {{.*}} 'Base::func' + // CHECK-FIXES: void func(); void func22(); // Should not warn. @@ -26,12 +28,42 @@ void fun(); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::fun' has {{.*}} 'Base::func' + // CHECK-FIXES: void func(); Derived &operator==(const Base &); // Should not warn: operators are ignored. virtual NoDefinedClass2 *f1(); // Should not crash: non-defined class return type is ignored. }; +template +struct TBase { + virtual void tfunc(T t); +}; + +template +struct TDerived : TBase { + virtual void tfunk(T t); + // Should not warn for 'TDerived::tfunk' or 'TDerived::tfunk', + // because matches in template instantiations are ignored. + // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: method 'TDerived::tfunk' has {{.*}} 'TBase::tfunc' + // CHECK-FIXES: virtual void tfunc(T t); +}; + +TDerived T1; +TDerived T2; + +// Should not fix macro definition +#define MACRO1 void funcM() +#define MACRO2(m) void m() +struct DerivedMacro : Base { + MACRO1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'DerivedMacro::funcM' has {{.*}} 'Base::func' + + MACRO2(func3); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'DerivedMacro::func3' has {{.*}} 'Base::func' + // CHECK-FIXES: MACRO2(func); +}; + typedef Derived derived_type; class Father { @@ -58,32 +90,40 @@ virtual void func2(); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::func2' has {{.*}} 'Father::func' + // CHECK-FIXES: virtual void func(); int methoe(int x, char **strs); // Should not warn: parameter types don't match. int methoe(int x); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::methoe' has {{.*}} 'Mother::method' + // CHECK-FIXES: int method(int x); 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' + // CHECK-FIXES: int method(int x, const char **strs); virtual Child *creat(int i); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::creat' has {{.*}} 'Father::create' + // CHECK-FIXES: virtual Child *create(int i); virtual Derived &&generat(); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::generat' has {{.*}} 'Father::generate' + // CHECK-FIXES: virtual Derived &&generate(); int decaz(const char str[]); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::decaz' has {{.*}} 'Mother::decay' + // CHECK-FIXES: int decay(const char str[]); operator bool(); derived_type *canonica(derived_type D); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::canonica' has {{.*}} 'Father::canonical' + // CHECK-FIXES: derived_type *canonical(derived_type D); private: void funk(); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::funk' has {{.*}} 'Father::func' + // CHECK-FIXES: void func(); };