| //===--- UseTransparentFunctorsCheck.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 "UseTransparentFunctorsCheck.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| |
| using namespace clang::ast_matchers; |
| |
| namespace clang { |
| namespace tidy { |
| namespace modernize { |
| |
| UseTransparentFunctorsCheck::UseTransparentFunctorsCheck( |
| StringRef Name, ClangTidyContext *Context) |
| : ClangTidyCheck(Name, Context), SafeMode(Options.get("SafeMode", 0)) {} |
| |
| void UseTransparentFunctorsCheck::storeOptions( |
| ClangTidyOptions::OptionMap &Opts) { |
| Options.store(Opts, "SafeMode", SafeMode ? 1 : 0); |
| } |
| |
| void UseTransparentFunctorsCheck::registerMatchers(MatchFinder *Finder) { |
| if (!getLangOpts().CPlusPlus14) |
| return; |
| |
| const auto TransparentFunctors = |
| classTemplateSpecializationDecl( |
| unless(hasAnyTemplateArgument(refersToType(voidType()))), |
| hasAnyName("::std::plus", "::std::minus", "::std::multiplies", |
| "::std::divides", "::std::modulus", "::std::negate", |
| "::std::equal_to", "::std::not_equal_to", "::std::greater", |
| "::std::less", "::std::greater_equal", "::std::less_equal", |
| "::std::logical_and", "::std::logical_or", |
| "::std::logical_not", "::std::bit_and", "::std::bit_or", |
| "::std::bit_xor", "::std::bit_not")) |
| .bind("FunctorClass"); |
| |
| // Non-transparent functor mentioned as a template parameter. FIXIT. |
| Finder->addMatcher( |
| loc(qualType( |
| unless(elaboratedType()), |
| hasDeclaration(classTemplateSpecializationDecl( |
| unless(hasAnyTemplateArgument(templateArgument(refersToType( |
| qualType(pointsTo(qualType(isAnyCharacter()))))))), |
| hasAnyTemplateArgument( |
| templateArgument(refersToType(qualType(hasDeclaration( |
| TransparentFunctors)))) |
| .bind("Functor")))))) |
| .bind("FunctorParentLoc"), |
| this); |
| |
| if (SafeMode) |
| return; |
| |
| // Non-transparent functor constructed. No FIXIT. There is no easy way |
| // to rule out the problematic char* vs string case. |
| Finder->addMatcher(cxxConstructExpr(hasDeclaration(cxxMethodDecl( |
| ofClass(TransparentFunctors))), |
| unless(isInTemplateInstantiation())) |
| .bind("FuncInst"), |
| this); |
| } |
| |
| static const StringRef Message = "prefer transparent functors '%0'"; |
| |
| template <typename T> static T getInnerTypeLocAs(TypeLoc Loc) { |
| T Result; |
| while (Result.isNull() && !Loc.isNull()) { |
| Result = Loc.getAs<T>(); |
| Loc = Loc.getNextTypeLoc(); |
| } |
| return Result; |
| } |
| |
| void UseTransparentFunctorsCheck::check( |
| const MatchFinder::MatchResult &Result) { |
| const auto *FuncClass = |
| Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("FunctorClass"); |
| if (const auto *FuncInst = |
| Result.Nodes.getNodeAs<CXXConstructExpr>("FuncInst")) { |
| diag(FuncInst->getLocStart(), Message) |
| << (FuncClass->getName() + "<>").str(); |
| return; |
| } |
| |
| const auto *Functor = Result.Nodes.getNodeAs<TemplateArgument>("Functor"); |
| const auto FunctorParentLoc = |
| Result.Nodes.getNodeAs<TypeLoc>("FunctorParentLoc") |
| ->getAs<TemplateSpecializationTypeLoc>(); |
| |
| if (!FunctorParentLoc) |
| return; |
| |
| unsigned ArgNum = 0; |
| const auto *FunctorParentType = |
| FunctorParentLoc.getType()->castAs<TemplateSpecializationType>(); |
| for (; ArgNum < FunctorParentType->getNumArgs(); ++ArgNum) { |
| const TemplateArgument &Arg = FunctorParentType->getArg(ArgNum); |
| if (Arg.getKind() != TemplateArgument::Type) |
| continue; |
| QualType ParentArgType = Arg.getAsType(); |
| if (ParentArgType->isRecordType() && |
| ParentArgType->getAsCXXRecordDecl() == |
| Functor->getAsType()->getAsCXXRecordDecl()) |
| break; |
| } |
| // Functor is a default template argument. |
| if (ArgNum == FunctorParentType->getNumArgs()) |
| return; |
| TemplateArgumentLoc FunctorLoc = FunctorParentLoc.getArgLoc(ArgNum); |
| auto FunctorTypeLoc = getInnerTypeLocAs<TemplateSpecializationTypeLoc>( |
| FunctorLoc.getTypeSourceInfo()->getTypeLoc()); |
| if (FunctorTypeLoc.isNull()) |
| return; |
| |
| SourceLocation ReportLoc = FunctorLoc.getLocation(); |
| diag(ReportLoc, Message) << (FuncClass->getName() + "<>").str() |
| << FixItHint::CreateRemoval( |
| FunctorTypeLoc.getArgLoc(0).getSourceRange()); |
| } |
| |
| } // namespace modernize |
| } // namespace tidy |
| } // namespace clang |