11#include "clang/AST/ASTContext.h"
12#include "clang/AST/DeclCXX.h"
13#include "clang/AST/ExprCXX.h"
14#include "clang/ASTMatchers/ASTMatchFinder.h"
20using llvm::SmallPtrSet;
24template <
typename S>
bool isSetDifferenceEmpty(
const S &S1,
const S &S2) {
32template <
typename Node>
33void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef
ID,
34 SmallPtrSet<const Node *, 16> &Nodes) {
35 for (
const auto &Match : Matches)
36 Nodes.insert(Match.getNodeAs<
Node>(
ID));
41bool isSameTypeIgnoringConst(QualType A, QualType B) {
42 A = A.getCanonicalType();
43 B = B.getCanonicalType();
50bool hasSameParameterTypes(
const CXXMethodDecl &D,
const CXXMethodDecl &O) {
51 if (D.getNumParams() != O.getNumParams())
53 for (
int I = 0,
E = D.getNumParams(); I <
E; ++I) {
54 if (!isSameTypeIgnoringConst(D.getParamDecl(I)->getType(),
55 O.getParamDecl(I)->getType()))
63const CXXMethodDecl *findConstOverload(
const CXXMethodDecl &D) {
66 DeclContext::lookup_result LookupResult =
67 D.getParent()->lookup(D.getNameInfo().getName());
68 if (LookupResult.isSingleResult()) {
72 for (
const Decl *Overload : LookupResult) {
73 const auto *O = dyn_cast<CXXMethodDecl>(Overload);
74 if (O && !O->isDeleted() && O->isConst() &&
75 O->getRefQualifier() == D.getRefQualifier() &&
76 hasSameParameterTypes(D, *O))
84bool pointsToSameTypeIgnoringConst(QualType A, QualType B) {
85 assert(A->isPointerType() || A->isReferenceType());
86 assert(B->isPointerType() || B->isReferenceType());
87 return isSameTypeIgnoringConst(A->getPointeeType(), B->getPointeeType());
125bool isLikelyShallowConst(
const CXXMethodDecl &
M) {
126 assert(!
M.isConst());
130 const CXXMethodDecl *ConstOverload = findConstOverload(
M);
131 if (ConstOverload ==
nullptr) {
136 const QualType CallTy =
M.getReturnType().getCanonicalType();
137 const QualType OverloadTy = ConstOverload->getReturnType().getCanonicalType();
138 if (CallTy->isReferenceType()) {
139 return OverloadTy->isReferenceType() &&
140 pointsToSameTypeIgnoringConst(CallTy, OverloadTy);
142 if (CallTy->isPointerType()) {
143 return OverloadTy->isPointerType() &&
144 pointsToSameTypeIgnoringConst(CallTy, OverloadTy);
146 return isSameTypeIgnoringConst(CallTy, OverloadTy);
166AST_MATCHER_P(DeclRefExpr, doesNotMutateObject,
int, Indirections) {
178 StackEntry(
const Expr *
E,
int Indirections)
179 :
E(
E), Indirections(Indirections) {}
187 llvm::SmallVector<StackEntry, 4> Stack;
188 Stack.emplace_back(&
Node, Indirections);
189 ASTContext &Ctx = Finder->getASTContext();
191 while (!Stack.empty()) {
192 const StackEntry
Entry = Stack.back();
197 QualType Ty =
Entry.E->getType().getCanonicalType();
198 for (
int I = 0; I <
Entry.Indirections; ++I) {
199 assert(Ty->isPointerType());
200 Ty = Ty->getPointeeType().getCanonicalType();
202 if (Ty->isVoidType() || Ty.isConstQualified())
207 const DynTypedNodeList Parents = Ctx.getParents(*
Entry.E);
211 for (
const auto &
Parent : Parents) {
212 if (
Parent.get<CompoundStmt>()) {
216 const Expr *
const P =
Parent.get<Expr>();
230 if (isa<ParenExpr>(P) || isa<MaterializeTemporaryExpr>(P)) {
231 Stack.emplace_back(P,
Entry.Indirections);
234 if (
const auto *
const Cast = dyn_cast<CastExpr>(P)) {
235 switch (Cast->getCastKind()) {
240 case CK_BaseToDerived:
241 case CK_DerivedToBase:
242 case CK_UncheckedDerivedToBase:
244 case CK_BaseToDerivedMemberPointer:
245 case CK_DerivedToBaseMemberPointer:
246 Stack.emplace_back(Cast,
Entry.Indirections);
249 case CK_PointerToBoolean:
252 case CK_LValueToRValue: {
254 if (
Entry.Indirections == 0)
256 Stack.emplace_back(Cast,
Entry.Indirections);
264 if (
const auto *
const Member = dyn_cast<MemberExpr>(P)) {
265 if (
const auto *
const Method =
266 dyn_cast<CXXMethodDecl>(Member->getMemberDecl())) {
267 if (Method->isConst() || Method->isStatic()) {
271 if (isLikelyShallowConst(*Method)) {
274 const auto MemberParents = Ctx.getParents(*Member);
275 assert(MemberParents.size() == 1);
276 const auto *Call = MemberParents[0].get<CallExpr>();
279 assert(Call !=
nullptr &&
"member function has to be called");
282 Method->getReturnType().getCanonicalType()->isPointerType()
289 Stack.emplace_back(Member, 0);
292 if (
const auto *
const OpCall = dyn_cast<CXXOperatorCallExpr>(P)) {
295 if (OpCall->getNumArgs() == 0 || OpCall->getArg(0) !=
Entry.E) {
298 const auto *
const Method =
299 dyn_cast_or_null<CXXMethodDecl>(OpCall->getDirectCallee());
301 if (Method ==
nullptr) {
307 if (Method->isConst() || Method->isStatic()) {
310 if (isLikelyShallowConst(*Method)) {
315 Method->getReturnType().getCanonicalType()->isPointerType() ? 1
322 if (
const auto *
const Op = dyn_cast<UnaryOperator>(P)) {
323 switch (Op->getOpcode()) {
325 Stack.emplace_back(Op,
Entry.Indirections + 1);
328 assert(
Entry.Indirections > 0);
329 Stack.emplace_back(Op,
Entry.Indirections - 1);
348SmallPtrSet<const DeclRefExpr *, 16>
350 ASTContext &Context,
int Indirections) {
351 auto Matches = match(findAll(declRefExpr(to(varDecl(equalsNode(&VarDecl))),
352 doesNotMutateObject(Indirections))
355 SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
356 extractNodesByIdTo(Matches,
"declRef", DeclRefs);
362 ASTContext &Context,
int Indirections) {
369 auto ConstReferenceDeclRefs =
371 return isSetDifferenceEmpty(AllDeclRefs, ConstReferenceDeclRefs);
374SmallPtrSet<const DeclRefExpr *, 16>
376 auto Matches = match(
377 findAll(declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind(
"declRef")),
379 SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
380 extractNodesByIdTo(Matches,
"declRef", DeclRefs);
384SmallPtrSet<const DeclRefExpr *, 16>
386 auto Matches = match(
387 decl(forEachDescendant(
388 declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind(
"declRef"))),
390 SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
391 extractNodesByIdTo(Matches,
"declRef", DeclRefs);
396 ASTContext &Context) {
397 auto UsedAsConstRefArg = forEachArgumentWithParam(
398 declRefExpr(equalsNode(&
DeclRef)),
399 parmVarDecl(hasType(matchers::isReferenceToConst())));
400 auto Matches = match(
402 cxxConstructExpr(UsedAsConstRefArg, hasDeclaration(cxxConstructorDecl(
403 isCopyConstructor())))
404 .bind(
"constructExpr"))),
406 return !Matches.empty();
410 ASTContext &Context) {
411 auto UsedAsConstRefArg = forEachArgumentWithParam(
412 declRefExpr(equalsNode(&
DeclRef)),
413 parmVarDecl(hasType(matchers::isReferenceToConst())));
414 auto Matches = match(
416 cxxOperatorCallExpr(UsedAsConstRefArg, hasOverloadedOperatorName(
"="),
417 callee(cxxMethodDecl(isCopyAssignmentOperator())))
418 .bind(
"operatorCallExpr"))),
420 return !Matches.empty();
const FunctionDecl * Decl
::clang::DynTypedNode Node
const google::protobuf::Message & M
const DeclRefExpr * DeclRef
AST_MATCHER_P(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
SmallPtrSet< const DeclRefExpr *, 16 > allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context)
Returns set of all DeclRefExprs to VarDecl within Stmt.
bool isCopyConstructorArgument(const DeclRefExpr &DeclRef, const Decl &Decl, ASTContext &Context)
Returns true if DeclRefExpr is the argument of a copy-constructor call expression within Decl.
bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt, ASTContext &Context, int Indirections)
Returns true if all DeclRefExpr to the variable within Stmt do not modify it.
bool isCopyAssignmentArgument(const DeclRefExpr &DeclRef, const Decl &Decl, ASTContext &Context)
Returns true if DeclRefExpr is the argument of a copy-assignment operator CallExpr within Decl.
SmallPtrSet< const DeclRefExpr *, 16 > constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context, int Indirections)
Returns set of all DeclRefExprs to VarDecl within Stmt where VarDecl is guaranteed to be accessed in ...