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 ? 1 : 0); 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) 85 << (FuncClass->getName() + "<>").str(); 86 return; 87 } 88 89 const auto *Functor = Result.Nodes.getNodeAs<TemplateArgument>("Functor"); 90 const auto FunctorParentLoc = 91 Result.Nodes.getNodeAs<TypeLoc>("FunctorParentLoc") 92 ->getAs<TemplateSpecializationTypeLoc>(); 93 94 if (!FunctorParentLoc) 95 return; 96 97 unsigned ArgNum = 0; 98 const auto *FunctorParentType = 99 FunctorParentLoc.getType()->castAs<TemplateSpecializationType>(); 100 for (; ArgNum < FunctorParentType->getNumArgs(); ++ArgNum) { 101 const TemplateArgument &Arg = FunctorParentType->getArg(ArgNum); 102 if (Arg.getKind() != TemplateArgument::Type) 103 continue; 104 QualType ParentArgType = Arg.getAsType(); 105 if (ParentArgType->isRecordType() && 106 ParentArgType->getAsCXXRecordDecl() == 107 Functor->getAsType()->getAsCXXRecordDecl()) 108 break; 109 } 110 // Functor is a default template argument. 111 if (ArgNum == FunctorParentType->getNumArgs()) 112 return; 113 TemplateArgumentLoc FunctorLoc = FunctorParentLoc.getArgLoc(ArgNum); 114 auto FunctorTypeLoc = getInnerTypeLocAs<TemplateSpecializationTypeLoc>( 115 FunctorLoc.getTypeSourceInfo()->getTypeLoc()); 116 if (FunctorTypeLoc.isNull()) 117 return; 118 119 SourceLocation ReportLoc = FunctorLoc.getLocation(); 120 if (ReportLoc.isInvalid()) 121 return; 122 diag(ReportLoc, Message) << (FuncClass->getName() + "<>").str() 123 << FixItHint::CreateRemoval( 124 FunctorTypeLoc.getArgLoc(0).getSourceRange()); 125 } 126 127 } // namespace modernize 128 } // namespace tidy 129 } // namespace clang 130