clang-tools 22.0.0git
ForwardingReferenceOverloadCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12
13using namespace clang::ast_matchers;
14
15namespace clang::tidy::bugprone {
16
17namespace {
18// Check if the given type is related to std::enable_if.
19AST_MATCHER(QualType, isEnableIf) {
20 auto CheckTemplate = [](const TemplateSpecializationType *Spec) {
21 if (!Spec)
22 return false;
23
24 const TemplateDecl *TDecl = Spec->getTemplateName().getAsTemplateDecl();
25
26 return TDecl && TDecl->isInStdNamespace() &&
27 (TDecl->getName() == "enable_if" ||
28 TDecl->getName() == "enable_if_t");
29 };
30 const Type *BaseType = Node.getTypePtr();
31 // Case: pointer or reference to enable_if.
32 while (BaseType->isPointerType() || BaseType->isReferenceType()) {
33 BaseType = BaseType->getPointeeType().getTypePtr();
34 }
35 // Case: type parameter dependent (enable_if<is_integral<T>>).
36 if (const auto *Dependent = BaseType->getAs<DependentNameType>())
37 BaseType = Dependent->getQualifier().getAsType();
38 if (!BaseType)
39 return false;
40 if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>()))
41 return true; // Case: enable_if_t< >.
42 if (const auto *TT = BaseType->getAs<TypedefType>())
43 if (NestedNameSpecifier Q = TT->getQualifier();
44 Q.getKind() == NestedNameSpecifier::Kind::Type)
45 if (CheckTemplate(Q.getAsType()->getAs<TemplateSpecializationType>()))
46 return true; // Case: enable_if< >::type.
47 return false;
48}
49AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
50 clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {
51 return Node.hasDefaultArgument() &&
52 TypeMatcher.matches(
53 Node.getDefaultArgument().getArgument().getAsType(), Finder,
54 Builder);
55}
56AST_MATCHER(TemplateDecl, hasAssociatedConstraints) {
57 return Node.hasAssociatedConstraints();
58}
59} // namespace
60
62 auto ForwardingRefParm =
63 parmVarDecl(
64 hasType(qualType(rValueReferenceType(),
65 references(templateTypeParmType(hasDeclaration(
66 templateTypeParmDecl().bind("type-parm-decl")))),
67 unless(references(isConstQualified())))))
68 .bind("parm-var");
69
70 DeclarationMatcher FindOverload =
71 cxxConstructorDecl(
72 hasParameter(0, ForwardingRefParm), unless(isDeleted()),
73 unless(hasAnyParameter(
74 // No warning: enable_if as constructor parameter.
75 parmVarDecl(hasType(isEnableIf())))),
76 unless(hasParent(functionTemplateDecl(anyOf(
77 // No warning: has associated constraints (like requires
78 // expression).
79 hasAssociatedConstraints(),
80 // No warning: enable_if as type parameter.
81 has(templateTypeParmDecl(hasDefaultArgument(isEnableIf()))),
82 // No warning: enable_if as non-type template parameter.
83 has(nonTypeTemplateParmDecl(
84 hasType(isEnableIf()),
85 anyOf(hasDescendant(cxxBoolLiteral()),
86 hasDescendant(cxxNullPtrLiteralExpr()),
87 hasDescendant(integerLiteral())))))))))
88 .bind("ctor");
89 Finder->addMatcher(FindOverload, this);
90}
91
93 const MatchFinder::MatchResult &Result) {
94 const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
95 const auto *TypeParmDecl =
96 Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
97
98 // Get the FunctionDecl and FunctionTemplateDecl containing the function
99 // parameter.
100 const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
101 if (!FuncForParam)
102 return;
103 const FunctionTemplateDecl *FuncTemplate =
104 FuncForParam->getDescribedFunctionTemplate();
105 if (!FuncTemplate)
106 return;
107
108 // Check that the template type parameter belongs to the same function
109 // template as the function parameter of that type. (This implies that type
110 // deduction will happen on the type.)
111 const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
112 if (!llvm::is_contained(*Params, TypeParmDecl))
113 return;
114
115 // Every parameter after the first must have a default value.
116 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
117 for (const auto *Param : llvm::drop_begin(Ctor->parameters())) {
118 if (!Param->hasDefaultArg())
119 return;
120 }
121 bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false,
122 DisabledMove = false;
123 for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
124 if (OtherCtor->isCopyOrMoveConstructor()) {
125 if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private)
126 (OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true;
127 else
128 (OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;
129 }
130 }
131 bool Copy = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy;
132 bool Move = !DisabledMove || EnabledMove;
133 if (!Copy && !Move)
134 return;
135 diag(Ctor->getLocation(),
136 "constructor accepting a forwarding reference can "
137 "hide the %select{copy|move|copy and move}0 constructor%s1")
138 << (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move;
139 for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
140 if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() &&
141 OtherCtor->getAccess() != AS_private) {
142 diag(OtherCtor->getLocation(),
143 "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note)
144 << OtherCtor->isMoveConstructor();
145 }
146 }
147}
148
149} // namespace clang::tidy::bugprone
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
@ Type
An inlay hint that for a type annotation.
Definition Protocol.h:1672
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
AST_MATCHER(BinaryOperator, isRelationalOperator)