xref: /llvm-project/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp (revision 16b3e43a030b0322e0d81debba3d63f145c8fd0b)
1 //===--- MissingStdForwardCheck.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 "MissingStdForwardCheck.h"
10 #include "../utils/Matchers.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Basic/IdentifierTable.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang::tidy::cppcoreguidelines {
18 
19 namespace {
20 
21 using matchers::hasUnevaluatedContext;
22 
AST_MATCHER_P(QualType,possiblyPackExpansionOf,ast_matchers::internal::Matcher<QualType>,InnerMatcher)23 AST_MATCHER_P(QualType, possiblyPackExpansionOf,
24               ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
25   return InnerMatcher.matches(Node.getNonPackExpansionType(), Finder, Builder);
26 }
27 
AST_MATCHER(ParmVarDecl,isTemplateTypeParameter)28 AST_MATCHER(ParmVarDecl, isTemplateTypeParameter) {
29   ast_matchers::internal::Matcher<QualType> Inner = possiblyPackExpansionOf(
30       qualType(rValueReferenceType(),
31                references(templateTypeParmType(
32                    hasDeclaration(templateTypeParmDecl()))),
33                unless(references(qualType(isConstQualified())))));
34   if (!Inner.matches(Node.getType(), Finder, Builder))
35     return false;
36 
37   const auto *Function = dyn_cast<FunctionDecl>(Node.getDeclContext());
38   if (!Function)
39     return false;
40 
41   const FunctionTemplateDecl *FuncTemplate =
42       Function->getDescribedFunctionTemplate();
43   if (!FuncTemplate)
44     return false;
45 
46   QualType ParamType =
47       Node.getType().getNonPackExpansionType()->getPointeeType();
48   const auto *TemplateType = ParamType->getAs<TemplateTypeParmType>();
49   if (!TemplateType)
50     return false;
51 
52   return TemplateType->getDepth() ==
53          FuncTemplate->getTemplateParameters()->getDepth();
54 }
55 
AST_MATCHER_P(NamedDecl,hasSameNameAsBoundNode,std::string,BindingID)56 AST_MATCHER_P(NamedDecl, hasSameNameAsBoundNode, std::string, BindingID) {
57   IdentifierInfo *II = Node.getIdentifier();
58   if (nullptr == II)
59     return false;
60   StringRef Name = II->getName();
61 
62   return Builder->removeBindings(
63       [this, Name](const ast_matchers::internal::BoundNodesMap &Nodes) {
64         const DynTypedNode &BN = Nodes.getNode(this->BindingID);
65         if (const auto *ND = BN.get<NamedDecl>()) {
66           if (!isa<FieldDecl, CXXMethodDecl, VarDecl>(ND))
67             return true;
68           return ND->getName() != Name;
69         }
70         return true;
71       });
72 }
73 
AST_MATCHER_P(LambdaCapture,hasCaptureKind,LambdaCaptureKind,Kind)74 AST_MATCHER_P(LambdaCapture, hasCaptureKind, LambdaCaptureKind, Kind) {
75   return Node.getCaptureKind() == Kind;
76 }
77 
AST_MATCHER_P(LambdaExpr,hasCaptureDefaultKind,LambdaCaptureDefault,Kind)78 AST_MATCHER_P(LambdaExpr, hasCaptureDefaultKind, LambdaCaptureDefault, Kind) {
79   return Node.getCaptureDefault() == Kind;
80 }
81 
AST_MATCHER(VarDecl,hasIdentifier)82 AST_MATCHER(VarDecl, hasIdentifier) {
83   const IdentifierInfo *ID = Node.getIdentifier();
84   return ID != NULL && !ID->isPlaceholder();
85 }
86 
87 } // namespace
88 
registerMatchers(MatchFinder * Finder)89 void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) {
90   auto RefToParmImplicit = allOf(
91       equalsBoundNode("var"), hasInitializer(ignoringParenImpCasts(
92                                   declRefExpr(to(equalsBoundNode("param"))))));
93   auto RefToParm = capturesVar(
94       varDecl(anyOf(hasSameNameAsBoundNode("param"), RefToParmImplicit)));
95   auto HasRefToParm = hasAnyCapture(RefToParm);
96 
97   auto CaptureInRef =
98       allOf(hasCaptureDefaultKind(LambdaCaptureDefault::LCD_ByRef),
99             unless(hasAnyCapture(
100                 capturesVar(varDecl(hasSameNameAsBoundNode("param"))))));
101   auto CaptureInCopy = allOf(
102       hasCaptureDefaultKind(LambdaCaptureDefault::LCD_ByCopy), HasRefToParm);
103   auto CaptureByRefExplicit = hasAnyCapture(
104       allOf(hasCaptureKind(LambdaCaptureKind::LCK_ByRef), RefToParm));
105 
106   auto CapturedInBody =
107       lambdaExpr(anyOf(CaptureInRef, CaptureInCopy, CaptureByRefExplicit));
108   auto CapturedInCaptureList = hasAnyCapture(capturesVar(
109       varDecl(hasInitializer(ignoringParenImpCasts(equalsBoundNode("call"))))));
110 
111   auto CapturedInLambda = hasDeclContext(cxxRecordDecl(
112       isLambda(),
113       hasParent(lambdaExpr(forCallable(equalsBoundNode("func")),
114                            anyOf(CapturedInCaptureList, CapturedInBody)))));
115 
116   auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param")));
117 
118   auto ForwardCallMatcher = callExpr(
119       callExpr().bind("call"), argumentCountIs(1),
120       hasArgument(0, declRefExpr(to(varDecl().bind("var")))),
121       forCallable(
122           anyOf(allOf(equalsBoundNode("func"),
123                       functionDecl(hasAnyParameter(parmVarDecl(allOf(
124                           equalsBoundNode("param"), equalsBoundNode("var")))))),
125                 CapturedInLambda)),
126       callee(unresolvedLookupExpr(hasAnyDeclaration(
127           namedDecl(hasUnderlyingDecl(hasName("::std::forward")))))),
128 
129       unless(anyOf(hasAncestor(typeLoc()),
130                    hasAncestor(expr(hasUnevaluatedContext())))));
131 
132   Finder->addMatcher(
133       parmVarDecl(
134           parmVarDecl().bind("param"), hasIdentifier(),
135           unless(hasAttr(attr::Kind::Unused)), isTemplateTypeParameter(),
136           hasAncestor(functionDecl().bind("func")),
137           hasAncestor(functionDecl(
138               isDefinition(), equalsBoundNode("func"), ToParam,
139               unless(anyOf(isDeleted(),
140                            hasDescendant(std::move(ForwardCallMatcher))))))),
141       this);
142 }
143 
check(const MatchFinder::MatchResult & Result)144 void MissingStdForwardCheck::check(const MatchFinder::MatchResult &Result) {
145   const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
146 
147   if (!Param)
148     return;
149 
150   diag(Param->getLocation(),
151        "forwarding reference parameter %0 is never forwarded "
152        "inside the function body")
153       << Param;
154 }
155 
156 } // namespace clang::tidy::cppcoreguidelines
157