forked from OSchip/llvm-project
151 lines
5.9 KiB
C++
151 lines
5.9 KiB
C++
//===--- ForwardingReferenceOverloadCheck.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 "ForwardingReferenceOverloadCheck.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
#include <algorithm>
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
namespace clang {
|
|
namespace tidy {
|
|
namespace bugprone {
|
|
|
|
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< >.
|
|
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);
|
|
}
|
|
} // namespace
|
|
|
|
void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {
|
|
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(anyOf(
|
|
// No warning: enable_if as type parameter.
|
|
has(templateTypeParmDecl(hasDefaultArgument(isEnableIf()))),
|
|
// No warning: enable_if as non-type template parameter.
|
|
has(nonTypeTemplateParmDecl(
|
|
hasType(isEnableIf()),
|
|
anyOf(hasDescendant(cxxBoolLiteral()),
|
|
hasDescendant(cxxNullPtrLiteralExpr()),
|
|
hasDescendant(integerLiteral())))))))))
|
|
.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 (!llvm::is_contained(*Params, TypeParmDecl))
|
|
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 = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy;
|
|
bool 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 bugprone
|
|
} // namespace tidy
|
|
} // namespace clang
|