
Parameter variable which is forwarded in lambda capture list or in body by reference is reasonable and current version of this check produces false positive on these cases. This patch try to fix the [issue](https://github.com/llvm/llvm-project/issues/68105) Co-authored-by: huqizhi <836744285@qq.com>
147 lines
5.2 KiB
C++
147 lines
5.2 KiB
C++
//===--- MissingStdForwardCheck.cpp - clang-tidy --------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MissingStdForwardCheck.h"
|
|
#include "../utils/Matchers.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/ExprConcepts.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
namespace clang::tidy::cppcoreguidelines {
|
|
|
|
namespace {
|
|
|
|
using matchers::hasUnevaluatedContext;
|
|
|
|
AST_MATCHER_P(QualType, possiblyPackExpansionOf,
|
|
ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
|
|
return InnerMatcher.matches(Node.getNonPackExpansionType(), Finder, Builder);
|
|
}
|
|
|
|
AST_MATCHER(ParmVarDecl, isTemplateTypeParameter) {
|
|
ast_matchers::internal::Matcher<QualType> Inner = possiblyPackExpansionOf(
|
|
qualType(rValueReferenceType(),
|
|
references(templateTypeParmType(
|
|
hasDeclaration(templateTypeParmDecl()))),
|
|
unless(references(qualType(isConstQualified())))));
|
|
if (!Inner.matches(Node.getType(), Finder, Builder))
|
|
return false;
|
|
|
|
const auto *Function = dyn_cast<FunctionDecl>(Node.getDeclContext());
|
|
if (!Function)
|
|
return false;
|
|
|
|
const FunctionTemplateDecl *FuncTemplate =
|
|
Function->getDescribedFunctionTemplate();
|
|
if (!FuncTemplate)
|
|
return false;
|
|
|
|
QualType ParamType =
|
|
Node.getType().getNonPackExpansionType()->getPointeeType();
|
|
const auto *TemplateType = ParamType->getAs<TemplateTypeParmType>();
|
|
if (!TemplateType)
|
|
return false;
|
|
|
|
return TemplateType->getDepth() ==
|
|
FuncTemplate->getTemplateParameters()->getDepth();
|
|
}
|
|
|
|
AST_MATCHER_P(NamedDecl, hasSameNameAsBoundNode, std::string, BindingID) {
|
|
IdentifierInfo *II = Node.getIdentifier();
|
|
if (nullptr == II)
|
|
return false;
|
|
StringRef Name = II->getName();
|
|
|
|
return Builder->removeBindings(
|
|
[this, Name](const ast_matchers::internal::BoundNodesMap &Nodes) {
|
|
const DynTypedNode &BN = Nodes.getNode(this->BindingID);
|
|
if (const auto *ND = BN.get<NamedDecl>()) {
|
|
if (!isa<FieldDecl, CXXMethodDecl, VarDecl>(ND))
|
|
return true;
|
|
return ND->getName() != Name;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
|
|
AST_MATCHER_P(LambdaCapture, hasCaptureKind, LambdaCaptureKind, Kind) {
|
|
return Node.getCaptureKind() == Kind;
|
|
}
|
|
|
|
AST_MATCHER_P(LambdaExpr, hasCaptureDefaultKind, LambdaCaptureDefault, Kind) {
|
|
return Node.getCaptureDefault() == Kind;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) {
|
|
auto RefToParmImplicit = allOf(
|
|
equalsBoundNode("var"), hasInitializer(ignoringParenImpCasts(
|
|
declRefExpr(to(equalsBoundNode("param"))))));
|
|
auto RefToParm = capturesVar(
|
|
varDecl(anyOf(hasSameNameAsBoundNode("param"), RefToParmImplicit)));
|
|
auto HasRefToParm = hasAnyCapture(RefToParm);
|
|
|
|
auto CaptureInRef =
|
|
allOf(hasCaptureDefaultKind(LambdaCaptureDefault::LCD_ByRef),
|
|
unless(hasAnyCapture(
|
|
capturesVar(varDecl(hasSameNameAsBoundNode("param"))))));
|
|
auto CaptureInCopy = allOf(
|
|
hasCaptureDefaultKind(LambdaCaptureDefault::LCD_ByCopy), HasRefToParm);
|
|
auto CaptureByRefExplicit = hasAnyCapture(
|
|
allOf(hasCaptureKind(LambdaCaptureKind::LCK_ByRef), RefToParm));
|
|
|
|
auto CapturedInBody =
|
|
lambdaExpr(anyOf(CaptureInRef, CaptureInCopy, CaptureByRefExplicit));
|
|
auto CapturedInCaptureList = hasAnyCapture(capturesVar(
|
|
varDecl(hasInitializer(ignoringParenImpCasts(equalsBoundNode("call"))))));
|
|
|
|
auto CapturedInLambda = hasDeclContext(cxxRecordDecl(
|
|
isLambda(),
|
|
hasParent(lambdaExpr(forCallable(equalsBoundNode("func")),
|
|
anyOf(CapturedInCaptureList, CapturedInBody)))));
|
|
|
|
auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param")));
|
|
|
|
auto ForwardCallMatcher = callExpr(
|
|
callExpr().bind("call"), argumentCountIs(1),
|
|
hasArgument(
|
|
0, declRefExpr(to(
|
|
varDecl(optionally(equalsBoundNode("param"))).bind("var")))),
|
|
forCallable(anyOf(equalsBoundNode("func"), CapturedInLambda)),
|
|
callee(unresolvedLookupExpr(hasAnyDeclaration(
|
|
namedDecl(hasUnderlyingDecl(hasName("::std::forward")))))),
|
|
|
|
unless(anyOf(hasAncestor(typeLoc()),
|
|
hasAncestor(expr(hasUnevaluatedContext())))));
|
|
|
|
Finder->addMatcher(
|
|
parmVarDecl(parmVarDecl().bind("param"), isTemplateTypeParameter(),
|
|
hasAncestor(functionDecl().bind("func")),
|
|
hasAncestor(functionDecl(
|
|
isDefinition(), equalsBoundNode("func"), ToParam,
|
|
unless(hasDescendant(std::move(ForwardCallMatcher)))))),
|
|
this);
|
|
}
|
|
|
|
void MissingStdForwardCheck::check(const MatchFinder::MatchResult &Result) {
|
|
const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
|
|
|
|
if (!Param)
|
|
return;
|
|
|
|
diag(Param->getLocation(),
|
|
"forwarding reference parameter %0 is never forwarded "
|
|
"inside the function body")
|
|
<< Param;
|
|
}
|
|
|
|
} // namespace clang::tidy::cppcoreguidelines
|