
Warns when a function accepting a forwarding reference does anything besides forwarding (with std::forward) the parameter in the body of the function. Reviewed By: PiotrZSL Differential Revision: https://reviews.llvm.org/D146921
91 lines
3.1 KiB
C++
91 lines
3.1 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();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) {
|
|
auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param")));
|
|
|
|
auto ForwardCallMatcher = callExpr(
|
|
forCallable(equalsBoundNode("func")), argumentCountIs(1),
|
|
callee(unresolvedLookupExpr(hasAnyDeclaration(
|
|
namedDecl(hasUnderlyingDecl(hasName("::std::forward")))))),
|
|
hasArgument(0, declRefExpr(to(equalsBoundNode("param"))).bind("ref")),
|
|
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
|