xref: /llvm-project/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp (revision 16b3e43a030b0322e0d81debba3d63f145c8fd0b)
15902bb95SChris Cotter //===--- MissingStdForwardCheck.cpp - clang-tidy --------------------------===//
25902bb95SChris Cotter //
35902bb95SChris Cotter // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45902bb95SChris Cotter // See https://llvm.org/LICENSE.txt for license information.
55902bb95SChris Cotter // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65902bb95SChris Cotter //
75902bb95SChris Cotter //===----------------------------------------------------------------------===//
85902bb95SChris Cotter 
95902bb95SChris Cotter #include "MissingStdForwardCheck.h"
105902bb95SChris Cotter #include "../utils/Matchers.h"
115902bb95SChris Cotter #include "clang/AST/ASTContext.h"
125902bb95SChris Cotter #include "clang/ASTMatchers/ASTMatchFinder.h"
13*16b3e43aSDanny Mösch #include "clang/Basic/IdentifierTable.h"
145902bb95SChris Cotter 
155902bb95SChris Cotter using namespace clang::ast_matchers;
165902bb95SChris Cotter 
175902bb95SChris Cotter namespace clang::tidy::cppcoreguidelines {
185902bb95SChris Cotter 
195902bb95SChris Cotter namespace {
205902bb95SChris Cotter 
215902bb95SChris Cotter using matchers::hasUnevaluatedContext;
225902bb95SChris Cotter 
AST_MATCHER_P(QualType,possiblyPackExpansionOf,ast_matchers::internal::Matcher<QualType>,InnerMatcher)235902bb95SChris Cotter AST_MATCHER_P(QualType, possiblyPackExpansionOf,
245902bb95SChris Cotter               ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
255902bb95SChris Cotter   return InnerMatcher.matches(Node.getNonPackExpansionType(), Finder, Builder);
265902bb95SChris Cotter }
275902bb95SChris Cotter 
AST_MATCHER(ParmVarDecl,isTemplateTypeParameter)285902bb95SChris Cotter AST_MATCHER(ParmVarDecl, isTemplateTypeParameter) {
295902bb95SChris Cotter   ast_matchers::internal::Matcher<QualType> Inner = possiblyPackExpansionOf(
305902bb95SChris Cotter       qualType(rValueReferenceType(),
315902bb95SChris Cotter                references(templateTypeParmType(
325902bb95SChris Cotter                    hasDeclaration(templateTypeParmDecl()))),
335902bb95SChris Cotter                unless(references(qualType(isConstQualified())))));
345902bb95SChris Cotter   if (!Inner.matches(Node.getType(), Finder, Builder))
355902bb95SChris Cotter     return false;
365902bb95SChris Cotter 
375902bb95SChris Cotter   const auto *Function = dyn_cast<FunctionDecl>(Node.getDeclContext());
385902bb95SChris Cotter   if (!Function)
395902bb95SChris Cotter     return false;
405902bb95SChris Cotter 
415902bb95SChris Cotter   const FunctionTemplateDecl *FuncTemplate =
425902bb95SChris Cotter       Function->getDescribedFunctionTemplate();
435902bb95SChris Cotter   if (!FuncTemplate)
445902bb95SChris Cotter     return false;
455902bb95SChris Cotter 
465902bb95SChris Cotter   QualType ParamType =
475902bb95SChris Cotter       Node.getType().getNonPackExpansionType()->getPointeeType();
485902bb95SChris Cotter   const auto *TemplateType = ParamType->getAs<TemplateTypeParmType>();
495902bb95SChris Cotter   if (!TemplateType)
505902bb95SChris Cotter     return false;
515902bb95SChris Cotter 
525902bb95SChris Cotter   return TemplateType->getDepth() ==
535902bb95SChris Cotter          FuncTemplate->getTemplateParameters()->getDepth();
545902bb95SChris Cotter }
555902bb95SChris Cotter 
AST_MATCHER_P(NamedDecl,hasSameNameAsBoundNode,std::string,BindingID)56d0848292SQizhi Hu AST_MATCHER_P(NamedDecl, hasSameNameAsBoundNode, std::string, BindingID) {
57d0848292SQizhi Hu   IdentifierInfo *II = Node.getIdentifier();
58d0848292SQizhi Hu   if (nullptr == II)
59d0848292SQizhi Hu     return false;
60d0848292SQizhi Hu   StringRef Name = II->getName();
61d0848292SQizhi Hu 
62d0848292SQizhi Hu   return Builder->removeBindings(
63d0848292SQizhi Hu       [this, Name](const ast_matchers::internal::BoundNodesMap &Nodes) {
64d0848292SQizhi Hu         const DynTypedNode &BN = Nodes.getNode(this->BindingID);
65d0848292SQizhi Hu         if (const auto *ND = BN.get<NamedDecl>()) {
66d0848292SQizhi Hu           if (!isa<FieldDecl, CXXMethodDecl, VarDecl>(ND))
67d0848292SQizhi Hu             return true;
68d0848292SQizhi Hu           return ND->getName() != Name;
69d0848292SQizhi Hu         }
70d0848292SQizhi Hu         return true;
71d0848292SQizhi Hu       });
72d0848292SQizhi Hu }
73d0848292SQizhi Hu 
AST_MATCHER_P(LambdaCapture,hasCaptureKind,LambdaCaptureKind,Kind)74d0848292SQizhi Hu AST_MATCHER_P(LambdaCapture, hasCaptureKind, LambdaCaptureKind, Kind) {
75d0848292SQizhi Hu   return Node.getCaptureKind() == Kind;
76d0848292SQizhi Hu }
77d0848292SQizhi Hu 
AST_MATCHER_P(LambdaExpr,hasCaptureDefaultKind,LambdaCaptureDefault,Kind)78d0848292SQizhi Hu AST_MATCHER_P(LambdaExpr, hasCaptureDefaultKind, LambdaCaptureDefault, Kind) {
79d0848292SQizhi Hu   return Node.getCaptureDefault() == Kind;
80d0848292SQizhi Hu }
81d0848292SQizhi Hu 
AST_MATCHER(VarDecl,hasIdentifier)82*16b3e43aSDanny Mösch AST_MATCHER(VarDecl, hasIdentifier) {
83*16b3e43aSDanny Mösch   const IdentifierInfo *ID = Node.getIdentifier();
84*16b3e43aSDanny Mösch   return ID != NULL && !ID->isPlaceholder();
85*16b3e43aSDanny Mösch }
86*16b3e43aSDanny Mösch 
875902bb95SChris Cotter } // namespace
885902bb95SChris Cotter 
registerMatchers(MatchFinder * Finder)895902bb95SChris Cotter void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) {
90d0848292SQizhi Hu   auto RefToParmImplicit = allOf(
91d0848292SQizhi Hu       equalsBoundNode("var"), hasInitializer(ignoringParenImpCasts(
92d0848292SQizhi Hu                                   declRefExpr(to(equalsBoundNode("param"))))));
93d0848292SQizhi Hu   auto RefToParm = capturesVar(
94d0848292SQizhi Hu       varDecl(anyOf(hasSameNameAsBoundNode("param"), RefToParmImplicit)));
95d0848292SQizhi Hu   auto HasRefToParm = hasAnyCapture(RefToParm);
96d0848292SQizhi Hu 
97d0848292SQizhi Hu   auto CaptureInRef =
98d0848292SQizhi Hu       allOf(hasCaptureDefaultKind(LambdaCaptureDefault::LCD_ByRef),
99d0848292SQizhi Hu             unless(hasAnyCapture(
100d0848292SQizhi Hu                 capturesVar(varDecl(hasSameNameAsBoundNode("param"))))));
101d0848292SQizhi Hu   auto CaptureInCopy = allOf(
102d0848292SQizhi Hu       hasCaptureDefaultKind(LambdaCaptureDefault::LCD_ByCopy), HasRefToParm);
103d0848292SQizhi Hu   auto CaptureByRefExplicit = hasAnyCapture(
104d0848292SQizhi Hu       allOf(hasCaptureKind(LambdaCaptureKind::LCK_ByRef), RefToParm));
105d0848292SQizhi Hu 
106d0848292SQizhi Hu   auto CapturedInBody =
107d0848292SQizhi Hu       lambdaExpr(anyOf(CaptureInRef, CaptureInCopy, CaptureByRefExplicit));
108d0848292SQizhi Hu   auto CapturedInCaptureList = hasAnyCapture(capturesVar(
109d0848292SQizhi Hu       varDecl(hasInitializer(ignoringParenImpCasts(equalsBoundNode("call"))))));
110d0848292SQizhi Hu 
111d0848292SQizhi Hu   auto CapturedInLambda = hasDeclContext(cxxRecordDecl(
112d0848292SQizhi Hu       isLambda(),
113d0848292SQizhi Hu       hasParent(lambdaExpr(forCallable(equalsBoundNode("func")),
114d0848292SQizhi Hu                            anyOf(CapturedInCaptureList, CapturedInBody)))));
115d0848292SQizhi Hu 
1165902bb95SChris Cotter   auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param")));
1175902bb95SChris Cotter 
1185902bb95SChris Cotter   auto ForwardCallMatcher = callExpr(
119d0848292SQizhi Hu       callExpr().bind("call"), argumentCountIs(1),
1208b326d59SQizhi Hu       hasArgument(0, declRefExpr(to(varDecl().bind("var")))),
1218b326d59SQizhi Hu       forCallable(
1228b326d59SQizhi Hu           anyOf(allOf(equalsBoundNode("func"),
1238b326d59SQizhi Hu                       functionDecl(hasAnyParameter(parmVarDecl(allOf(
1248b326d59SQizhi Hu                           equalsBoundNode("param"), equalsBoundNode("var")))))),
1258b326d59SQizhi Hu                 CapturedInLambda)),
1265902bb95SChris Cotter       callee(unresolvedLookupExpr(hasAnyDeclaration(
1275902bb95SChris Cotter           namedDecl(hasUnderlyingDecl(hasName("::std::forward")))))),
128d0848292SQizhi Hu 
1295902bb95SChris Cotter       unless(anyOf(hasAncestor(typeLoc()),
1305902bb95SChris Cotter                    hasAncestor(expr(hasUnevaluatedContext())))));
1315902bb95SChris Cotter 
1325902bb95SChris Cotter   Finder->addMatcher(
133*16b3e43aSDanny Mösch       parmVarDecl(
134*16b3e43aSDanny Mösch           parmVarDecl().bind("param"), hasIdentifier(),
135*16b3e43aSDanny Mösch           unless(hasAttr(attr::Kind::Unused)), isTemplateTypeParameter(),
1365902bb95SChris Cotter           hasAncestor(functionDecl().bind("func")),
1375902bb95SChris Cotter           hasAncestor(functionDecl(
1385902bb95SChris Cotter               isDefinition(), equalsBoundNode("func"), ToParam,
139*16b3e43aSDanny Mösch               unless(anyOf(isDeleted(),
140*16b3e43aSDanny Mösch                            hasDescendant(std::move(ForwardCallMatcher))))))),
1415902bb95SChris Cotter       this);
1425902bb95SChris Cotter }
1435902bb95SChris Cotter 
check(const MatchFinder::MatchResult & Result)1445902bb95SChris Cotter void MissingStdForwardCheck::check(const MatchFinder::MatchResult &Result) {
1455902bb95SChris Cotter   const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
1465902bb95SChris Cotter 
1475902bb95SChris Cotter   if (!Param)
1485902bb95SChris Cotter     return;
1495902bb95SChris Cotter 
1505902bb95SChris Cotter   diag(Param->getLocation(),
1515902bb95SChris Cotter        "forwarding reference parameter %0 is never forwarded "
1525902bb95SChris Cotter        "inside the function body")
1535902bb95SChris Cotter       << Param;
1545902bb95SChris Cotter }
1555902bb95SChris Cotter 
1565902bb95SChris Cotter } // namespace clang::tidy::cppcoreguidelines
157