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