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
;
19 UseTransparentFunctorsCheck::UseTransparentFunctorsCheck(
20 StringRef Name
, ClangTidyContext
*Context
)
21 : ClangTidyCheck(Name
, Context
), SafeMode(Options
.get("SafeMode", false)) {}
23 void UseTransparentFunctorsCheck::storeOptions(
24 ClangTidyOptions::OptionMap
&Opts
) {
25 Options
.store(Opts
, "SafeMode", SafeMode
);
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");
41 // Non-transparent functor mentioned as a template parameter. FIXIT.
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
))))
52 .bind("FunctorParentLoc"),
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()))
67 static const StringRef Message
= "prefer transparent functors '%0<>'";
69 template <typename T
> static T
getInnerTypeLocAs(TypeLoc Loc
) {
71 while (Result
.isNull() && !Loc
.isNull()) {
72 Result
= Loc
.getAs
<T
>();
73 Loc
= Loc
.getNextTypeLoc();
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();
88 const auto *Functor
= Result
.Nodes
.getNodeAs
<TemplateArgument
>("Functor");
89 const auto FunctorParentLoc
=
90 Result
.Nodes
.getNodeAs
<TypeLoc
>("FunctorParentLoc")
91 ->getAs
<TemplateSpecializationTypeLoc
>();
93 if (!FunctorParentLoc
)
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
)
103 QualType ParentArgType
= Arg
.getAsType();
104 if (ParentArgType
->isRecordType() &&
105 ParentArgType
->getAsCXXRecordDecl() ==
106 Functor
->getAsType()->getAsCXXRecordDecl())
109 // Functor is a default template argument.
110 if (ArgNum
== FunctorParentType
->getNumArgs())
112 TemplateArgumentLoc FunctorLoc
= FunctorParentLoc
.getArgLoc(ArgNum
);
113 auto FunctorTypeLoc
= getInnerTypeLocAs
<TemplateSpecializationTypeLoc
>(
114 FunctorLoc
.getTypeSourceInfo()->getTypeLoc());
115 if (FunctorTypeLoc
.isNull())
118 SourceLocation ReportLoc
= FunctorLoc
.getLocation();
119 if (ReportLoc
.isInvalid())
121 diag(ReportLoc
, Message
) << FuncClass
->getName()
122 << FixItHint::CreateRemoval(
123 FunctorTypeLoc
.getArgLoc(0).getSourceRange());
126 } // namespace modernize