xref: /llvm-project/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp (revision 72a28a3bf0b539bcdfd8f41905675ce6a890c0ac)
154309b1cSJan Voung #include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h"
254309b1cSJan Voung 
354309b1cSJan Voung #include "clang/AST/CanonicalType.h"
454309b1cSJan Voung #include "clang/AST/DeclCXX.h"
554309b1cSJan Voung #include "clang/ASTMatchers/ASTMatchers.h"
654309b1cSJan Voung #include "clang/ASTMatchers/ASTMatchersMacros.h"
754309b1cSJan Voung #include "clang/Basic/OperatorKinds.h"
854309b1cSJan Voung 
954309b1cSJan Voung namespace clang::dataflow {
1054309b1cSJan Voung 
1154309b1cSJan Voung namespace {
1254309b1cSJan Voung 
1354309b1cSJan Voung using ast_matchers::callee;
1454309b1cSJan Voung using ast_matchers::cxxMemberCallExpr;
1554309b1cSJan Voung using ast_matchers::cxxMethodDecl;
1654309b1cSJan Voung using ast_matchers::cxxOperatorCallExpr;
1754309b1cSJan Voung using ast_matchers::hasName;
1854309b1cSJan Voung using ast_matchers::hasOverloadedOperatorName;
1954309b1cSJan Voung using ast_matchers::ofClass;
2054309b1cSJan Voung using ast_matchers::parameterCountIs;
2154309b1cSJan Voung using ast_matchers::pointerType;
2254309b1cSJan Voung using ast_matchers::referenceType;
2354309b1cSJan Voung using ast_matchers::returns;
2454309b1cSJan Voung 
2554309b1cSJan Voung bool hasSmartPointerClassShape(const CXXRecordDecl &RD, bool &HasGet,
2654309b1cSJan Voung                                bool &HasValue) {
2754309b1cSJan Voung   // We may want to cache this search, but in current profiles it hasn't shown
2854309b1cSJan Voung   // up as a hot spot (possibly because there aren't many hits, relatively).
2954309b1cSJan Voung   bool HasArrow = false;
3054309b1cSJan Voung   bool HasStar = false;
3154309b1cSJan Voung   CanQualType StarReturnType, ArrowReturnType, GetReturnType, ValueReturnType;
3254309b1cSJan Voung   for (const auto *MD : RD.methods()) {
3354309b1cSJan Voung     // We only consider methods that are const and have zero parameters.
3454309b1cSJan Voung     // It may be that there is a non-const overload for the method, but
3554309b1cSJan Voung     // there should at least be a const overload as well.
3654309b1cSJan Voung     if (!MD->isConst() || MD->getNumParams() != 0)
3754309b1cSJan Voung       continue;
3854309b1cSJan Voung     switch (MD->getOverloadedOperator()) {
3954309b1cSJan Voung     case OO_Star:
4054309b1cSJan Voung       if (MD->getReturnType()->isReferenceType()) {
4154309b1cSJan Voung         HasStar = true;
4254309b1cSJan Voung         StarReturnType = MD->getReturnType()
4354309b1cSJan Voung                              .getNonReferenceType()
4454309b1cSJan Voung                              ->getCanonicalTypeUnqualified();
4554309b1cSJan Voung       }
4654309b1cSJan Voung       break;
4754309b1cSJan Voung     case OO_Arrow:
4854309b1cSJan Voung       if (MD->getReturnType()->isPointerType()) {
4954309b1cSJan Voung         HasArrow = true;
5054309b1cSJan Voung         ArrowReturnType = MD->getReturnType()
5154309b1cSJan Voung                               ->getPointeeType()
5254309b1cSJan Voung                               ->getCanonicalTypeUnqualified();
5354309b1cSJan Voung       }
5454309b1cSJan Voung       break;
5554309b1cSJan Voung     case OO_None: {
5654309b1cSJan Voung       IdentifierInfo *II = MD->getIdentifier();
5754309b1cSJan Voung       if (II == nullptr)
5854309b1cSJan Voung         continue;
5954309b1cSJan Voung       if (II->isStr("get")) {
6054309b1cSJan Voung         if (MD->getReturnType()->isPointerType()) {
6154309b1cSJan Voung           HasGet = true;
6254309b1cSJan Voung           GetReturnType = MD->getReturnType()
6354309b1cSJan Voung                               ->getPointeeType()
6454309b1cSJan Voung                               ->getCanonicalTypeUnqualified();
6554309b1cSJan Voung         }
6654309b1cSJan Voung       } else if (II->isStr("value")) {
6754309b1cSJan Voung         if (MD->getReturnType()->isReferenceType()) {
6854309b1cSJan Voung           HasValue = true;
6954309b1cSJan Voung           ValueReturnType = MD->getReturnType()
7054309b1cSJan Voung                                 .getNonReferenceType()
7154309b1cSJan Voung                                 ->getCanonicalTypeUnqualified();
7254309b1cSJan Voung         }
7354309b1cSJan Voung       }
742d5dc5c2SJan Voung     } break;
7554309b1cSJan Voung     default:
7654309b1cSJan Voung       break;
7754309b1cSJan Voung     }
7854309b1cSJan Voung   }
7954309b1cSJan Voung 
8054309b1cSJan Voung   if (!HasStar || !HasArrow || StarReturnType != ArrowReturnType)
8154309b1cSJan Voung     return false;
8254309b1cSJan Voung   HasGet = HasGet && (GetReturnType == StarReturnType);
8354309b1cSJan Voung   HasValue = HasValue && (ValueReturnType == StarReturnType);
8454309b1cSJan Voung   return true;
8554309b1cSJan Voung }
8654309b1cSJan Voung 
8754309b1cSJan Voung } // namespace
8854309b1cSJan Voung } // namespace clang::dataflow
8954309b1cSJan Voung 
9054309b1cSJan Voung // AST_MATCHER macros create an "internal" namespace, so we put it in
9154309b1cSJan Voung // its own anonymous namespace instead of in clang::dataflow.
9254309b1cSJan Voung namespace {
9354309b1cSJan Voung 
9454309b1cSJan Voung AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGet) {
9554309b1cSJan Voung   bool HasGet = false;
9654309b1cSJan Voung   bool HasValue = false;
9754309b1cSJan Voung   bool HasStarAndArrow =
9854309b1cSJan Voung       clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
9954309b1cSJan Voung   return HasStarAndArrow && HasGet;
10054309b1cSJan Voung }
10154309b1cSJan Voung 
10254309b1cSJan Voung AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithValue) {
10354309b1cSJan Voung   bool HasGet = false;
10454309b1cSJan Voung   bool HasValue = false;
10554309b1cSJan Voung   bool HasStarAndArrow =
10654309b1cSJan Voung       clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
10754309b1cSJan Voung   return HasStarAndArrow && HasValue;
10854309b1cSJan Voung }
10954309b1cSJan Voung 
11054309b1cSJan Voung AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) {
11154309b1cSJan Voung   bool HasGet = false;
11254309b1cSJan Voung   bool HasValue = false;
11354309b1cSJan Voung   bool HasStarAndArrow =
11454309b1cSJan Voung       clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
11554309b1cSJan Voung   return HasStarAndArrow && (HasGet || HasValue);
11654309b1cSJan Voung }
11754309b1cSJan Voung 
11854309b1cSJan Voung } // namespace
11954309b1cSJan Voung 
12054309b1cSJan Voung namespace clang::dataflow {
12154309b1cSJan Voung 
12254309b1cSJan Voung ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar() {
12354309b1cSJan Voung   return cxxOperatorCallExpr(
12454309b1cSJan Voung       hasOverloadedOperatorName("*"),
12554309b1cSJan Voung       callee(cxxMethodDecl(parameterCountIs(0), returns(referenceType()),
12654309b1cSJan Voung                            ofClass(smartPointerClassWithGetOrValue()))));
12754309b1cSJan Voung }
12854309b1cSJan Voung 
12954309b1cSJan Voung ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() {
13054309b1cSJan Voung   return cxxOperatorCallExpr(
13154309b1cSJan Voung       hasOverloadedOperatorName("->"),
13254309b1cSJan Voung       callee(cxxMethodDecl(parameterCountIs(0), returns(pointerType()),
13354309b1cSJan Voung                            ofClass(smartPointerClassWithGetOrValue()))));
13454309b1cSJan Voung }
135*72a28a3bSJan Voung 
13654309b1cSJan Voung ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall() {
13754309b1cSJan Voung   return cxxMemberCallExpr(callee(
13854309b1cSJan Voung       cxxMethodDecl(parameterCountIs(0), returns(referenceType()),
13954309b1cSJan Voung                     hasName("value"), ofClass(smartPointerClassWithValue()))));
14054309b1cSJan Voung }
14154309b1cSJan Voung 
14254309b1cSJan Voung ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall() {
14354309b1cSJan Voung   return cxxMemberCallExpr(callee(
14454309b1cSJan Voung       cxxMethodDecl(parameterCountIs(0), returns(pointerType()), hasName("get"),
14554309b1cSJan Voung                     ofClass(smartPointerClassWithGet()))));
14654309b1cSJan Voung }
14754309b1cSJan Voung 
148*72a28a3bSJan Voung const FunctionDecl *
149*72a28a3bSJan Voung getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE) {
150*72a28a3bSJan Voung   const FunctionDecl *CanonicalCallee = nullptr;
151*72a28a3bSJan Voung   const CXXMethodDecl *Callee =
152*72a28a3bSJan Voung       cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
153*72a28a3bSJan Voung   if (Callee == nullptr)
154*72a28a3bSJan Voung     return nullptr;
155*72a28a3bSJan Voung   const CXXRecordDecl *RD = Callee->getParent();
156*72a28a3bSJan Voung   if (RD == nullptr)
157*72a28a3bSJan Voung     return nullptr;
158*72a28a3bSJan Voung   for (const auto *MD : RD->methods()) {
159*72a28a3bSJan Voung     if (MD->getOverloadedOperator() == OO_Star && MD->isConst() &&
160*72a28a3bSJan Voung         MD->getNumParams() == 0 && MD->getReturnType()->isReferenceType()) {
161*72a28a3bSJan Voung       CanonicalCallee = MD;
162*72a28a3bSJan Voung       break;
163*72a28a3bSJan Voung     }
164*72a28a3bSJan Voung   }
165*72a28a3bSJan Voung   return CanonicalCallee;
166*72a28a3bSJan Voung }
167*72a28a3bSJan Voung 
16854309b1cSJan Voung } // namespace clang::dataflow
169