1 //=======- PtrTypesSemantics.cpp ---------------------------------*- C++ -*-==//
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 
9 #include "PtrTypesSemantics.h"
10 #include "ASTUtils.h"
11 #include "clang/AST/CXXInheritance.h"
12 #include "clang/AST/Decl.h"
13 #include "clang/AST/DeclCXX.h"
14 #include "clang/AST/ExprCXX.h"
15 #include <optional>
16 
17 using namespace clang;
18 
19 namespace {
20 
hasPublicMethodInBaseClass(const CXXRecordDecl * R,const char * NameToMatch)21 bool hasPublicMethodInBaseClass(const CXXRecordDecl *R,
22                                 const char *NameToMatch) {
23   assert(R);
24   assert(R->hasDefinition());
25 
26   for (const CXXMethodDecl *MD : R->methods()) {
27     const auto MethodName = safeGetName(MD);
28     if (MethodName == NameToMatch && MD->getAccess() == AS_public)
29       return true;
30   }
31   return false;
32 }
33 
34 } // namespace
35 
36 namespace clang {
37 
38 std::optional<const clang::CXXRecordDecl *>
hasPublicMethodInBase(const CXXBaseSpecifier * Base,const char * NameToMatch)39 hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) {
40   assert(Base);
41 
42   const Type *T = Base->getType().getTypePtrOrNull();
43   if (!T)
44     return std::nullopt;
45 
46   const CXXRecordDecl *R = T->getAsCXXRecordDecl();
47   if (!R)
48     return std::nullopt;
49   if (!R->hasDefinition())
50     return std::nullopt;
51 
52   return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr;
53 }
54 
isRefCountable(const CXXRecordDecl * R)55 std::optional<bool> isRefCountable(const CXXRecordDecl* R)
56 {
57   assert(R);
58 
59   R = R->getDefinition();
60   if (!R)
61     return std::nullopt;
62 
63   bool hasRef = hasPublicMethodInBaseClass(R, "ref");
64   bool hasDeref = hasPublicMethodInBaseClass(R, "deref");
65   if (hasRef && hasDeref)
66     return true;
67 
68   CXXBasePaths Paths;
69   Paths.setOrigin(const_cast<CXXRecordDecl *>(R));
70 
71   bool AnyInconclusiveBase = false;
72   const auto hasPublicRefInBase =
73       [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
74         auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
75         if (!hasRefInBase) {
76           AnyInconclusiveBase = true;
77           return false;
78         }
79         return (*hasRefInBase) != nullptr;
80       };
81 
82   hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths,
83                                       /*LookupInDependent =*/true);
84   if (AnyInconclusiveBase)
85     return std::nullopt;
86 
87   const auto hasPublicDerefInBase =
88       [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
89         auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
90         if (!hasDerefInBase) {
91           AnyInconclusiveBase = true;
92           return false;
93         }
94         return (*hasDerefInBase) != nullptr;
95       };
96   hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths,
97                                           /*LookupInDependent =*/true);
98   if (AnyInconclusiveBase)
99     return std::nullopt;
100 
101   return hasRef && hasDeref;
102 }
103 
isCtorOfRefCounted(const clang::FunctionDecl * F)104 bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
105   assert(F);
106   const auto &FunctionName = safeGetName(F);
107 
108   return FunctionName == "Ref" || FunctionName == "makeRef"
109 
110          || FunctionName == "RefPtr" || FunctionName == "makeRefPtr"
111 
112          || FunctionName == "UniqueRef" || FunctionName == "makeUniqueRef" ||
113          FunctionName == "makeUniqueRefWithoutFastMallocCheck"
114 
115          || FunctionName == "String" || FunctionName == "AtomString" ||
116          FunctionName == "UniqueString"
117          // FIXME: Implement as attribute.
118          || FunctionName == "Identifier";
119 }
120 
isUncounted(const CXXRecordDecl * Class)121 std::optional<bool> isUncounted(const CXXRecordDecl* Class)
122 {
123   // Keep isRefCounted first as it's cheaper.
124   if (isRefCounted(Class))
125     return false;
126 
127   std::optional<bool> IsRefCountable = isRefCountable(Class);
128   if (!IsRefCountable)
129     return std::nullopt;
130 
131   return (*IsRefCountable);
132 }
133 
isUncountedPtr(const Type * T)134 std::optional<bool> isUncountedPtr(const Type* T)
135 {
136   assert(T);
137 
138   if (T->isPointerType() || T->isReferenceType()) {
139     if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
140       return isUncounted(CXXRD);
141     }
142   }
143   return false;
144 }
145 
isGetterOfRefCounted(const CXXMethodDecl * M)146 std::optional<bool> isGetterOfRefCounted(const CXXMethodDecl* M)
147 {
148   assert(M);
149 
150   if (isa<CXXMethodDecl>(M)) {
151     const CXXRecordDecl *calleeMethodsClass = M->getParent();
152     auto className = safeGetName(calleeMethodsClass);
153     auto methodName = safeGetName(M);
154 
155     if (((className == "Ref" || className == "RefPtr") &&
156          methodName == "get") ||
157         ((className == "String" || className == "AtomString" ||
158           className == "AtomStringImpl" || className == "UniqueString" ||
159           className == "UniqueStringImpl" || className == "Identifier") &&
160          methodName == "impl"))
161       return true;
162 
163     // Ref<T> -> T conversion
164     // FIXME: Currently allowing any Ref<T> -> whatever cast.
165     if (className == "Ref" || className == "RefPtr") {
166       if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) {
167         if (auto *targetConversionType =
168                 maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) {
169           return isUncountedPtr(targetConversionType);
170         }
171       }
172     }
173   }
174   return false;
175 }
176 
isRefCounted(const CXXRecordDecl * R)177 bool isRefCounted(const CXXRecordDecl *R) {
178   assert(R);
179   if (auto *TmplR = R->getTemplateInstantiationPattern()) {
180     // FIXME: String/AtomString/UniqueString
181     const auto &ClassName = safeGetName(TmplR);
182     return ClassName == "RefPtr" || ClassName == "Ref";
183   }
184   return false;
185 }
186 
isPtrConversion(const FunctionDecl * F)187 bool isPtrConversion(const FunctionDecl *F) {
188   assert(F);
189   if (isCtorOfRefCounted(F))
190     return true;
191 
192   // FIXME: check # of params == 1
193   const auto FunctionName = safeGetName(F);
194   if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
195       FunctionName == "dynamicDowncast"
196       || FunctionName == "downcast" || FunctionName == "bitwise_cast")
197     return true;
198 
199   return false;
200 }
201 
202 } // namespace clang
203