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