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