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