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