1 //===--- UseTransparentFunctorsCheck.cpp - clang-tidy----------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "UseTransparentFunctorsCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang { 17 namespace tidy { 18 namespace modernize { 19 20 UseTransparentFunctorsCheck::UseTransparentFunctorsCheck( 21 StringRef Name, ClangTidyContext *Context) 22 : ClangTidyCheck(Name, Context), SafeMode(Options.get("SafeMode", 0)) {} 23 24 void UseTransparentFunctorsCheck::storeOptions( 25 ClangTidyOptions::OptionMap &Opts) { 26 Options.store(Opts, "SafeMode", SafeMode ? 1 : 0); 27 } 28 29 void UseTransparentFunctorsCheck::registerMatchers(MatchFinder *Finder) { 30 if (!getLangOpts().CPlusPlus14) 31 return; 32 33 const auto TransparentFunctors = 34 classTemplateSpecializationDecl( 35 unless(hasAnyTemplateArgument(refersToType(voidType()))), 36 hasAnyName("::std::plus", "::std::minus", "::std::multiplies", 37 "::std::divides", "::std::modulus", "::std::negate", 38 "::std::equal_to", "::std::not_equal_to", "::std::greater", 39 "::std::less", "::std::greater_equal", "::std::less_equal", 40 "::std::logical_and", "::std::logical_or", 41 "::std::logical_not", "::std::bit_and", "::std::bit_or", 42 "::std::bit_xor", "::std::bit_not")) 43 .bind("FunctorClass"); 44 45 // Non-transparent functor mentioned as a template parameter. FIXIT. 46 Finder->addMatcher( 47 loc(qualType( 48 unless(elaboratedType()), 49 hasDeclaration(classTemplateSpecializationDecl( 50 unless(hasAnyTemplateArgument(templateArgument(refersToType( 51 qualType(pointsTo(qualType(isAnyCharacter()))))))), 52 hasAnyTemplateArgument( 53 templateArgument(refersToType(qualType(hasDeclaration( 54 TransparentFunctors)))) 55 .bind("Functor")))))) 56 .bind("FunctorParentLoc"), 57 this); 58 59 if (SafeMode) 60 return; 61 62 // Non-transparent functor constructed. No FIXIT. There is no easy way 63 // to rule out the problematic char* vs string case. 64 Finder->addMatcher(cxxConstructExpr(hasDeclaration(cxxMethodDecl( 65 ofClass(TransparentFunctors))), 66 unless(isInTemplateInstantiation())) 67 .bind("FuncInst"), 68 this); 69 } 70 71 static const StringRef Message = "prefer transparent functors '%0'"; 72 73 template <typename T> static T getInnerTypeLocAs(TypeLoc Loc) { 74 T Result; 75 while (Result.isNull() && !Loc.isNull()) { 76 Result = Loc.getAs<T>(); 77 Loc = Loc.getNextTypeLoc(); 78 } 79 return Result; 80 } 81 82 void UseTransparentFunctorsCheck::check( 83 const MatchFinder::MatchResult &Result) { 84 const auto *FuncClass = 85 Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("FunctorClass"); 86 if (const auto *FuncInst = 87 Result.Nodes.getNodeAs<CXXConstructExpr>("FuncInst")) { 88 diag(FuncInst->getLocStart(), Message) 89 << (FuncClass->getName() + "<>").str(); 90 return; 91 } 92 93 const auto *Functor = Result.Nodes.getNodeAs<TemplateArgument>("Functor"); 94 const auto FunctorParentLoc = 95 Result.Nodes.getNodeAs<TypeLoc>("FunctorParentLoc") 96 ->getAs<TemplateSpecializationTypeLoc>(); 97 98 if (!FunctorParentLoc) 99 return; 100 101 unsigned ArgNum = 0; 102 const auto *FunctorParentType = 103 FunctorParentLoc.getType()->castAs<TemplateSpecializationType>(); 104 for (; ArgNum < FunctorParentType->getNumArgs(); ++ArgNum) { 105 const TemplateArgument &Arg = FunctorParentType->getArg(ArgNum); 106 if (Arg.getKind() != TemplateArgument::Type) 107 continue; 108 QualType ParentArgType = Arg.getAsType(); 109 if (ParentArgType->isRecordType() && 110 ParentArgType->getAsCXXRecordDecl() == 111 Functor->getAsType()->getAsCXXRecordDecl()) 112 break; 113 } 114 // Functor is a default template argument. 115 if (ArgNum == FunctorParentType->getNumArgs()) 116 return; 117 TemplateArgumentLoc FunctorLoc = FunctorParentLoc.getArgLoc(ArgNum); 118 auto FunctorTypeLoc = getInnerTypeLocAs<TemplateSpecializationTypeLoc>( 119 FunctorLoc.getTypeSourceInfo()->getTypeLoc()); 120 if (FunctorTypeLoc.isNull()) 121 return; 122 123 SourceLocation ReportLoc = FunctorLoc.getLocation(); 124 diag(ReportLoc, Message) << (FuncClass->getName() + "<>").str() 125 << FixItHint::CreateRemoval( 126 FunctorTypeLoc.getArgLoc(0).getSourceRange()); 127 } 128 129 } // namespace modernize 130 } // namespace tidy 131 } // namespace clang 132