1 //===--- DeclRefExprUtils.cpp - clang-tidy---------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "DeclRefExprUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/DeclCXX.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 namespace clang::tidy::utils::decl_ref_expr
{
17 using namespace ::clang::ast_matchers
;
18 using llvm::SmallPtrSet
;
22 template <typename S
> bool isSetDifferenceEmpty(const S
&S1
, const S
&S2
) {
29 // Extracts all Nodes keyed by ID from Matches and inserts them into Nodes.
30 template <typename Node
>
31 void extractNodesByIdTo(ArrayRef
<BoundNodes
> Matches
, StringRef ID
,
32 SmallPtrSet
<const Node
*, 16> &Nodes
) {
33 for (const auto &Match
: Matches
)
34 Nodes
.insert(Match
.getNodeAs
<Node
>(ID
));
39 // Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl
40 // is the a const reference or value argument to a CallExpr or CXXConstructExpr.
41 SmallPtrSet
<const DeclRefExpr
*, 16>
42 constReferenceDeclRefExprs(const VarDecl
&VarDecl
, const Stmt
&Stmt
,
43 ASTContext
&Context
) {
45 declRefExpr(to(varDecl(equalsNode(&VarDecl
)))).bind("declRef");
46 auto ConstMethodCallee
= callee(cxxMethodDecl(isConst()));
47 // Match method call expressions where the variable is referenced as the this
48 // implicit object argument and operator call expression for member operators
49 // where the variable is the 0-th argument.
51 findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee
, on(DeclRefToVar
)),
52 cxxOperatorCallExpr(ConstMethodCallee
,
53 hasArgument(0, DeclRefToVar
))))),
55 SmallPtrSet
<const DeclRefExpr
*, 16> DeclRefs
;
56 extractNodesByIdTo(Matches
, "declRef", DeclRefs
);
57 auto ConstReferenceOrValue
=
58 qualType(anyOf(matchers::isReferenceToConst(),
59 unless(anyOf(referenceType(), pointerType(),
60 substTemplateTypeParmType()))));
61 auto ConstReferenceOrValueOrReplaced
= qualType(anyOf(
62 ConstReferenceOrValue
,
63 substTemplateTypeParmType(hasReplacementType(ConstReferenceOrValue
))));
64 auto UsedAsConstRefOrValueArg
= forEachArgumentWithParam(
65 DeclRefToVar
, parmVarDecl(hasType(ConstReferenceOrValueOrReplaced
)));
66 Matches
= match(findAll(invocation(UsedAsConstRefOrValueArg
)), Stmt
, Context
);
67 extractNodesByIdTo(Matches
, "declRef", DeclRefs
);
68 // References and pointers to const assignments.
70 match(findAll(declStmt(
71 has(varDecl(hasType(qualType(matchers::isReferenceToConst())),
72 hasInitializer(ignoringImpCasts(DeclRefToVar
)))))),
74 extractNodesByIdTo(Matches
, "declRef", DeclRefs
);
76 match(findAll(declStmt(has(varDecl(
77 hasType(qualType(matchers::isPointerToConst())),
78 hasInitializer(ignoringImpCasts(unaryOperator(
79 hasOperatorName("&"), hasUnaryOperand(DeclRefToVar
)))))))),
81 extractNodesByIdTo(Matches
, "declRef", DeclRefs
);
85 bool isOnlyUsedAsConst(const VarDecl
&Var
, const Stmt
&Stmt
,
86 ASTContext
&Context
) {
87 // Collect all DeclRefExprs to the loop variable and all CallExprs and
88 // CXXConstructExprs where the loop variable is used as argument to a const
89 // reference parameter.
90 // If the difference is empty it is safe for the loop variable to be a const
92 auto AllDeclRefs
= allDeclRefExprs(Var
, Stmt
, Context
);
93 auto ConstReferenceDeclRefs
= constReferenceDeclRefExprs(Var
, Stmt
, Context
);
94 return isSetDifferenceEmpty(AllDeclRefs
, ConstReferenceDeclRefs
);
97 SmallPtrSet
<const DeclRefExpr
*, 16>
98 allDeclRefExprs(const VarDecl
&VarDecl
, const Stmt
&Stmt
, ASTContext
&Context
) {
100 findAll(declRefExpr(to(varDecl(equalsNode(&VarDecl
)))).bind("declRef")),
102 SmallPtrSet
<const DeclRefExpr
*, 16> DeclRefs
;
103 extractNodesByIdTo(Matches
, "declRef", DeclRefs
);
107 SmallPtrSet
<const DeclRefExpr
*, 16>
108 allDeclRefExprs(const VarDecl
&VarDecl
, const Decl
&Decl
, ASTContext
&Context
) {
109 auto Matches
= match(
110 decl(forEachDescendant(
111 declRefExpr(to(varDecl(equalsNode(&VarDecl
)))).bind("declRef"))),
113 SmallPtrSet
<const DeclRefExpr
*, 16> DeclRefs
;
114 extractNodesByIdTo(Matches
, "declRef", DeclRefs
);
118 bool isCopyConstructorArgument(const DeclRefExpr
&DeclRef
, const Decl
&Decl
,
119 ASTContext
&Context
) {
120 auto UsedAsConstRefArg
= forEachArgumentWithParam(
121 declRefExpr(equalsNode(&DeclRef
)),
122 parmVarDecl(hasType(matchers::isReferenceToConst())));
123 auto Matches
= match(
125 cxxConstructExpr(UsedAsConstRefArg
, hasDeclaration(cxxConstructorDecl(
126 isCopyConstructor())))
127 .bind("constructExpr"))),
129 return !Matches
.empty();
132 bool isCopyAssignmentArgument(const DeclRefExpr
&DeclRef
, const Decl
&Decl
,
133 ASTContext
&Context
) {
134 auto UsedAsConstRefArg
= forEachArgumentWithParam(
135 declRefExpr(equalsNode(&DeclRef
)),
136 parmVarDecl(hasType(matchers::isReferenceToConst())));
137 auto Matches
= match(
139 cxxOperatorCallExpr(UsedAsConstRefArg
, hasOverloadedOperatorName("="),
140 callee(cxxMethodDecl(isCopyAssignmentOperator())))
141 .bind("operatorCallExpr"))),
143 return !Matches
.empty();
146 } // namespace clang::tidy::utils::decl_ref_expr