blob: 1e81a1a434953233d038e788e137c498fb7c3210 [file] [log] [blame]
//===--- ForwardingReferenceOverloadCheck.cpp - clang-tidy-----------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ForwardingReferenceOverloadCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include <algorithm>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace misc {
namespace {
// Check if the given type is related to std::enable_if.
AST_MATCHER(QualType, isEnableIf) {
auto CheckTemplate = [](const TemplateSpecializationType *Spec) {
if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) {
return false;
}
const NamedDecl *TypeDecl =
Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl();
return TypeDecl->isInStdNamespace() &&
(TypeDecl->getName().equals("enable_if") ||
TypeDecl->getName().equals("enable_if_t"));
};
const Type *BaseType = Node.getTypePtr();
// Case: pointer or reference to enable_if.
while (BaseType->isPointerType() || BaseType->isReferenceType()) {
BaseType = BaseType->getPointeeType().getTypePtr();
}
// Case: type parameter dependent (enable_if<is_integral<T>>).
if (const auto *Dependent = BaseType->getAs<DependentNameType>()) {
BaseType = Dependent->getQualifier()->getAsType();
}
if (!BaseType)
return false;
if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>())) {
return true; // Case: enable_if_t< >.
} else if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) {
if (const auto *Qualifier = Elaborated->getQualifier()->getAsType()) {
if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) {
return true; // Case: enable_if< >::type.
}
}
}
return false;
}
AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {
return Node.hasDefaultArgument() &&
TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder);
}
}
void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {
// Forwarding references require C++11 or later.
if (!getLangOpts().CPlusPlus11)
return;
auto ForwardingRefParm =
parmVarDecl(
hasType(qualType(rValueReferenceType(),
references(templateTypeParmType(hasDeclaration(
templateTypeParmDecl().bind("type-parm-decl")))),
unless(references(isConstQualified())))))
.bind("parm-var");
DeclarationMatcher findOverload =
cxxConstructorDecl(
hasParameter(0, ForwardingRefParm),
unless(hasAnyParameter(
// No warning: enable_if as constructor parameter.
parmVarDecl(hasType(isEnableIf())))),
unless(hasParent(functionTemplateDecl(has(templateTypeParmDecl(
// No warning: enable_if as type parameter.
hasDefaultArgument(isEnableIf())))))))
.bind("ctor");
Finder->addMatcher(findOverload, this);
}
void ForwardingReferenceOverloadCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
const auto *TypeParmDecl =
Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
// Get the FunctionDecl and FunctionTemplateDecl containing the function
// parameter.
const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
if (!FuncForParam)
return;
const FunctionTemplateDecl *FuncTemplate =
FuncForParam->getDescribedFunctionTemplate();
if (!FuncTemplate)
return;
// Check that the template type parameter belongs to the same function
// template as the function parameter of that type. (This implies that type
// deduction will happen on the type.)
const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
if (std::find(Params->begin(), Params->end(), TypeParmDecl) == Params->end())
return;
// Every parameter after the first must have a default value.
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
for (auto Iter = Ctor->param_begin() + 1; Iter != Ctor->param_end(); ++Iter) {
if (!(*Iter)->hasDefaultArg())
return;
}
bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false,
DisabledMove = false;
for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
if (OtherCtor->isCopyOrMoveConstructor()) {
if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private)
(OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true;
else
(OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;
}
}
bool Copy = !DisabledCopy || EnabledCopy, Move = !DisabledMove || EnabledMove;
if (!Copy && !Move)
return;
diag(Ctor->getLocation(),
"constructor accepting a forwarding reference can "
"hide the %select{copy|move|copy and move}0 constructor%s1")
<< (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move;
for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() &&
OtherCtor->getAccess() != AS_private) {
diag(OtherCtor->getLocation(),
"%select{copy|move}0 constructor declared here", DiagnosticIDs::Note)
<< OtherCtor->isMoveConstructor();
}
}
}
} // namespace misc
} // namespace tidy
} // namespace clang