1 //===--- UseTransparentFunctorsCheck.cpp - clang-tidy----------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "UseTransparentFunctorsCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 using namespace clang::ast_matchers
;
15 namespace clang::tidy::modernize
{
17 UseTransparentFunctorsCheck::UseTransparentFunctorsCheck(
18 StringRef Name
, ClangTidyContext
*Context
)
19 : ClangTidyCheck(Name
, Context
), SafeMode(Options
.get("SafeMode", false)) {}
21 void UseTransparentFunctorsCheck::storeOptions(
22 ClangTidyOptions::OptionMap
&Opts
) {
23 Options
.store(Opts
, "SafeMode", SafeMode
);
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");
39 // Non-transparent functor mentioned as a template parameter. FIXIT.
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
))))
50 .bind("FunctorParentLoc"),
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()))
65 static const StringRef Message
= "prefer transparent functors '%0<>'";
67 template <typename T
> static T
getInnerTypeLocAs(TypeLoc Loc
) {
69 while (Result
.isNull() && !Loc
.isNull()) {
70 Result
= Loc
.getAs
<T
>();
71 Loc
= Loc
.getNextTypeLoc();
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(), Message
) << FuncClass
->getName();
86 const auto *Functor
= Result
.Nodes
.getNodeAs
<TemplateArgument
>("Functor");
87 const auto FunctorParentLoc
=
88 Result
.Nodes
.getNodeAs
<TypeLoc
>("FunctorParentLoc")
89 ->getAs
<TemplateSpecializationTypeLoc
>();
91 if (!FunctorParentLoc
)
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
)
102 QualType ParentArgType
= Arg
.getAsType();
103 if (ParentArgType
->isRecordType() &&
104 ParentArgType
->getAsCXXRecordDecl() ==
105 Functor
->getAsType()->getAsCXXRecordDecl())
108 // Functor is a default template argument.
109 if (ArgNum
== FunctorParentType
->template_arguments().size())
111 TemplateArgumentLoc FunctorLoc
= FunctorParentLoc
.getArgLoc(ArgNum
);
112 auto FunctorTypeLoc
= getInnerTypeLocAs
<TemplateSpecializationTypeLoc
>(
113 FunctorLoc
.getTypeSourceInfo()->getTypeLoc());
114 if (FunctorTypeLoc
.isNull())
117 SourceLocation ReportLoc
= FunctorLoc
.getLocation();
118 if (ReportLoc
.isInvalid())
120 diag(ReportLoc
, Message
) << FuncClass
->getName()
121 << FixItHint::CreateRemoval(
122 FunctorTypeLoc
.getArgLoc(0).getSourceRange());
125 } // namespace clang::tidy::modernize