200 lines · cpp
1#include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h"2 3#include "clang/AST/CanonicalType.h"4#include "clang/AST/DeclCXX.h"5#include "clang/AST/Type.h"6#include "clang/ASTMatchers/ASTMatchers.h"7#include "clang/ASTMatchers/ASTMatchersMacros.h"8#include "clang/Basic/OperatorKinds.h"9 10namespace clang::dataflow {11 12namespace {13 14using ast_matchers::callee;15using ast_matchers::cxxMemberCallExpr;16using ast_matchers::cxxMethodDecl;17using ast_matchers::cxxOperatorCallExpr;18using ast_matchers::hasCanonicalType;19using ast_matchers::hasName;20using ast_matchers::hasOverloadedOperatorName;21using ast_matchers::ofClass;22using ast_matchers::parameterCountIs;23using ast_matchers::pointerType;24using ast_matchers::referenceType;25using ast_matchers::returns;26 27CanQualType getLikeReturnType(QualType RT) {28 if (!RT.isNull() && RT->isPointerType()) {29 return RT->getPointeeType()30 ->getCanonicalTypeUnqualified()31 .getUnqualifiedType();32 }33 return {};34}35 36CanQualType valueLikeReturnType(QualType RT) {37 if (!RT.isNull() && RT->isReferenceType()) {38 return RT.getNonReferenceType()39 ->getCanonicalTypeUnqualified()40 .getUnqualifiedType();41 }42 return {};43}44 45CanQualType pointerLikeReturnType(const CXXRecordDecl &RD) {46 // We may want to cache this search, but in current profiles it hasn't shown47 // up as a hot spot (possibly because there aren't many hits, relatively).48 CanQualType StarReturnType, ArrowReturnType;49 for (const auto *MD : RD.methods()) {50 // We only consider methods that are const and have zero parameters.51 // It may be that there is a non-const overload for the method, but52 // there should at least be a const overload as well.53 if (!MD->isConst() || MD->getNumParams() != 0)54 continue;55 switch (MD->getOverloadedOperator()) {56 case OO_Star:57 StarReturnType = valueLikeReturnType(MD->getReturnType());58 break;59 case OO_Arrow:60 ArrowReturnType = getLikeReturnType(MD->getReturnType());61 break;62 default:63 break;64 }65 }66 if (!StarReturnType.isNull() && !ArrowReturnType.isNull() &&67 StarReturnType == ArrowReturnType)68 return StarReturnType;69 70 return {};71}72 73QualType findReturnType(const CXXRecordDecl &RD, StringRef MethodName) {74 for (const auto *MD : RD.methods()) {75 // We only consider methods that are const and have zero parameters.76 // It may be that there is a non-const overload for the method, but77 // there should at least be a const overload as well.78 if (!MD->isConst() || MD->getNumParams() != 0 ||79 MD->getOverloadedOperator() != OO_None)80 continue;81 clang::IdentifierInfo *II = MD->getIdentifier();82 if (II && II->isStr(MethodName))83 return MD->getReturnType();84 }85 return {};86}87 88} // namespace89} // namespace clang::dataflow90 91// AST_MATCHER macros create an "internal" namespace, so we put it in92// its own anonymous namespace instead of in clang::dataflow.93namespace {94 95using clang::dataflow::findReturnType;96using clang::dataflow::getLikeReturnType;97using clang::dataflow::pointerLikeReturnType;98using clang::dataflow::valueLikeReturnType;99 100AST_MATCHER_P(clang::CXXRecordDecl, smartPointerClassWithGetLike,101 clang::StringRef, MethodName) {102 auto RT = pointerLikeReturnType(Node);103 if (RT.isNull())104 return false;105 return getLikeReturnType(findReturnType(Node, MethodName)) == RT;106}107 108AST_MATCHER_P(clang::CXXRecordDecl, smartPointerClassWithValueLike,109 clang::StringRef, MethodName) {110 auto RT = pointerLikeReturnType(Node);111 if (RT.isNull())112 return false;113 return valueLikeReturnType(findReturnType(Node, MethodName)) == RT;114}115 116AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) {117 auto RT = pointerLikeReturnType(Node);118 if (RT.isNull())119 return false;120 return getLikeReturnType(findReturnType(Node, "get")) == RT ||121 valueLikeReturnType(findReturnType(Node, "value")) == RT;122}123 124AST_MATCHER(clang::CXXRecordDecl, pointerClass) {125 return !pointerLikeReturnType(Node).isNull();126}127 128} // namespace129 130namespace clang::dataflow {131 132ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar() {133 return cxxOperatorCallExpr(134 hasOverloadedOperatorName("*"),135 callee(cxxMethodDecl(parameterCountIs(0),136 returns(hasCanonicalType(referenceType())),137 ofClass(smartPointerClassWithGetOrValue()))));138}139 140ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() {141 return cxxOperatorCallExpr(142 hasOverloadedOperatorName("->"),143 callee(cxxMethodDecl(parameterCountIs(0),144 returns(hasCanonicalType(pointerType())),145 ofClass(smartPointerClassWithGetOrValue()))));146}147 148ast_matchers::StatementMatcher isPointerLikeOperatorStar() {149 return cxxOperatorCallExpr(150 hasOverloadedOperatorName("*"),151 callee(cxxMethodDecl(parameterCountIs(0),152 returns(hasCanonicalType(referenceType())),153 ofClass(pointerClass()))));154}155 156ast_matchers::StatementMatcher isPointerLikeOperatorArrow() {157 return cxxOperatorCallExpr(158 hasOverloadedOperatorName("->"),159 callee(cxxMethodDecl(parameterCountIs(0),160 returns(hasCanonicalType(pointerType())),161 ofClass(pointerClass()))));162}163 164ast_matchers::StatementMatcher165isSmartPointerLikeValueMethodCall(clang::StringRef MethodName) {166 return cxxMemberCallExpr(callee(cxxMethodDecl(167 parameterCountIs(0), returns(hasCanonicalType(referenceType())),168 hasName(MethodName),169 ofClass(smartPointerClassWithValueLike(MethodName)))));170}171 172ast_matchers::StatementMatcher173isSmartPointerLikeGetMethodCall(clang::StringRef MethodName) {174 return cxxMemberCallExpr(callee(cxxMethodDecl(175 parameterCountIs(0), returns(hasCanonicalType(pointerType())),176 hasName(MethodName), ofClass(smartPointerClassWithGetLike(MethodName)))));177}178 179const FunctionDecl *180getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE) {181 const FunctionDecl *CanonicalCallee = nullptr;182 const CXXMethodDecl *Callee =183 cast_or_null<CXXMethodDecl>(CE->getDirectCallee());184 if (Callee == nullptr)185 return nullptr;186 const CXXRecordDecl *RD = Callee->getParent();187 if (RD == nullptr)188 return nullptr;189 for (const auto *MD : RD->methods()) {190 if (MD->getOverloadedOperator() == OO_Star && MD->isConst() &&191 MD->getNumParams() == 0 && MD->getReturnType()->isReferenceType()) {192 CanonicalCallee = MD;193 break;194 }195 }196 return CanonicalCallee;197}198 199} // namespace clang::dataflow200