|  | // Part of the Crubit project, under the Apache License v2.0 with LLVM | 
|  | // Exceptions. See /LICENSE for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  |  | 
|  | #include "nullability/pointer_nullability_matchers.h" | 
|  |  | 
|  | #include "clang/AST/DeclCXX.h" | 
|  | #include "clang/AST/OperationKinds.h" | 
|  | #include "clang/AST/Stmt.h" | 
|  | #include "clang/ASTMatchers/ASTMatchers.h" | 
|  | #include "clang/ASTMatchers/ASTMatchersInternal.h" | 
|  | #include "llvm/ADT/ArrayRef.h" | 
|  | #include "llvm/ADT/SmallVector.h" | 
|  | #include "llvm/ADT/StringRef.h" | 
|  | #include "llvm/ADT/iterator.h" | 
|  |  | 
|  | namespace clang::tidy::nullability { | 
|  |  | 
|  | using ast_matchers::anyOf; | 
|  | using ast_matchers::argumentCountIs; | 
|  | using ast_matchers::arraySubscriptExpr; | 
|  | using ast_matchers::binaryOperator; | 
|  | using ast_matchers::booleanType; | 
|  | using ast_matchers::callee; | 
|  | using ast_matchers::callExpr; | 
|  | using ast_matchers::compoundStmt; | 
|  | using ast_matchers::cxxConstructExpr; | 
|  | using ast_matchers::cxxConversionDecl; | 
|  | using ast_matchers::cxxCtorInitializer; | 
|  | using ast_matchers::cxxMemberCallExpr; | 
|  | using ast_matchers::cxxMethodDecl; | 
|  | using ast_matchers::cxxOperatorCallExpr; | 
|  | using ast_matchers::cxxRecordDecl; | 
|  | using ast_matchers::cxxThisExpr; | 
|  | using ast_matchers::decl; | 
|  | using ast_matchers::expr; | 
|  | using ast_matchers::functionDecl; | 
|  | using ast_matchers::has; | 
|  | using ast_matchers::hasAnyName; | 
|  | using ast_matchers::hasAnyOperatorName; | 
|  | using ast_matchers::hasAnyOverloadedOperatorName; | 
|  | using ast_matchers::hasArgument; | 
|  | using ast_matchers::hasBase; | 
|  | using ast_matchers::hasBody; | 
|  | using ast_matchers::hasCanonicalType; | 
|  | using ast_matchers::hasCastKind; | 
|  | using ast_matchers::hasDeclaration; | 
|  | using ast_matchers::hasName; | 
|  | using ast_matchers::hasObjectExpression; | 
|  | using ast_matchers::hasOperands; | 
|  | using ast_matchers::hasOperatorName; | 
|  | using ast_matchers::hasOverloadedOperatorName; | 
|  | using ast_matchers::hasReturnValue; | 
|  | using ast_matchers::hasType; | 
|  | using ast_matchers::hasUnaryOperand; | 
|  | using ast_matchers::ignoringParenImpCasts; | 
|  | using ast_matchers::implicitCastExpr; | 
|  | using ast_matchers::isArrow; | 
|  | using ast_matchers::isConst; | 
|  | using ast_matchers::isInStdNamespace; | 
|  | using ast_matchers::isInteger; | 
|  | using ast_matchers::isMemberInitializer; | 
|  | using ast_matchers::memberExpr; | 
|  | using ast_matchers::parameterCountIs; | 
|  | using ast_matchers::pointee; | 
|  | using ast_matchers::pointerType; | 
|  | using ast_matchers::qualType; | 
|  | using ast_matchers::returnStmt; | 
|  | using ast_matchers::statementCountIs; | 
|  | using ast_matchers::thisPointerType; | 
|  | using ast_matchers::unaryOperator; | 
|  | using ast_matchers::unless; | 
|  | using ast_matchers::internal::Matcher; | 
|  |  | 
|  | Matcher<Stmt> isPointerExpr() { return expr(hasType(isSupportedRawPointer())); } | 
|  | Matcher<Stmt> isNullPointerLiteral() { | 
|  | return implicitCastExpr(anyOf(hasCastKind(CK_NullToPointer), | 
|  | hasCastKind(CK_NullToMemberPointer))); | 
|  | } | 
|  | Matcher<Stmt> isAddrOf() { return unaryOperator(hasOperatorName("&")); } | 
|  | Matcher<Stmt> isPointerDereference() { | 
|  | return unaryOperator(hasOperatorName("*"), hasUnaryOperand(isPointerExpr())); | 
|  | } | 
|  | Matcher<Stmt> isPointerSubscript() { | 
|  | return arraySubscriptExpr(hasBase(isPointerExpr())); | 
|  | } | 
|  | Matcher<Stmt> isPointerCheckBinOp() { | 
|  | return binaryOperator(hasAnyOperatorName("!=", "=="), | 
|  | hasOperands(isPointerExpr(), isPointerExpr())); | 
|  | } | 
|  | Matcher<Stmt> isPointerIncOrDec() { | 
|  | return unaryOperator(hasAnyOperatorName("++", "--"), | 
|  | hasUnaryOperand(isPointerExpr())); | 
|  | } | 
|  | Matcher<Stmt> isPointerAddOrSubAssign() { | 
|  | return binaryOperator(hasAnyOperatorName("+=", "-="), | 
|  | hasOperands(isPointerExpr(), hasType(isInteger()))); | 
|  | } | 
|  | Matcher<Stmt> isImplicitCastPointerToBool() { | 
|  | return implicitCastExpr(hasCastKind(CK_PointerToBoolean)); | 
|  | } | 
|  | Matcher<Stmt> isMemberOfPointerType() { | 
|  | return memberExpr(hasType(isSupportedRawPointer())); | 
|  | } | 
|  | Matcher<Stmt> isPointerArrow() { return memberExpr(isArrow()); } | 
|  | Matcher<Stmt> isPointerReturn() { | 
|  | return returnStmt(hasReturnValue(hasType(isSupportedPointer()))); | 
|  | } | 
|  | Matcher<CXXCtorInitializer> isCtorMemberInitializer() { | 
|  | return cxxCtorInitializer(isMemberInitializer()); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isZeroParamConstMemberCall() { | 
|  | return cxxMemberCallExpr( | 
|  | callee(cxxMethodDecl(parameterCountIs(0), isConst()))); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isZeroParamConstMemberOperatorCall() { | 
|  | return cxxOperatorCallExpr( | 
|  | callee(cxxMethodDecl(parameterCountIs(0), isConst()))); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isNonConstMemberCall() { | 
|  | return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst())))); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isNonConstMemberOperatorCall() { | 
|  | return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst())))); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isSmartPointerArrowMemberExpr() { | 
|  | return memberExpr(hasObjectExpression(hasType(qualType( | 
|  | hasCanonicalType(pointerType(pointee(isSupportedSmartPointer()))))))); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isSmartPointerConstructor() { | 
|  | return cxxConstructExpr(hasType(isSupportedSmartPointer())); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isSmartPointerOperatorCall(llvm::StringRef Name, int NumArgs) { | 
|  | return cxxOperatorCallExpr( | 
|  | hasOverloadedOperatorName(Name), argumentCountIs(NumArgs), | 
|  | hasArgument(0, hasType(isSupportedSmartPointer()))); | 
|  | } | 
|  |  | 
|  | namespace internal { | 
|  |  | 
|  | ast_matchers::internal::Matcher<Stmt> isSmartPointerMethodCallFunc( | 
|  | llvm::ArrayRef<const llvm::StringRef*> NameRefs) { | 
|  | return cxxMemberCallExpr(thisPointerType(isSupportedSmartPointer()), | 
|  | callee(cxxMethodDecl(hasAnyName(llvm::to_vector<2>( | 
|  | llvm::make_pointee_range(NameRefs)))))); | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  |  | 
|  | const ast_matchers::internal::VariadicFunction< | 
|  | ast_matchers::internal::Matcher<Stmt>, llvm::StringRef, | 
|  | internal::isSmartPointerMethodCallFunc> | 
|  | isSmartPointerMethodCall = {}; | 
|  |  | 
|  | Matcher<Stmt> isSmartPointerFreeSwapCall() { | 
|  | return callExpr(callee(functionDecl(isInStdNamespace(), hasName("swap"))), | 
|  | argumentCountIs(2), | 
|  | hasArgument(0, hasType(isSupportedSmartPointer())), | 
|  | hasArgument(1, hasType(isSupportedSmartPointer()))); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isSmartPointerBoolConversionCall() { | 
|  | return cxxMemberCallExpr(thisPointerType(isSupportedSmartPointer()), | 
|  | callee(cxxConversionDecl()), hasType(booleanType())); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isSmartPointerFactoryCall() { | 
|  | return callExpr( | 
|  | hasType(isSupportedSmartPointer()), | 
|  | callee(functionDecl( | 
|  | isInStdNamespace(), | 
|  | hasAnyName("make_unique", "make_unique_for_overwrite", "make_shared", | 
|  | "make_shared_for_overwrite", "allocate_shared", | 
|  | "allocate_shared_for_overwrite")))); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isWrapUniqueCall() { | 
|  | return callExpr( | 
|  | hasType(isSupportedSmartPointer()), | 
|  | callee(functionDecl(hasName("WrapUnique"), isInAbslNamespace()))); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isSmartPointerComparisonOpCall() { | 
|  | return cxxOperatorCallExpr( | 
|  | hasAnyOverloadedOperatorName("==", "!="), argumentCountIs(2), | 
|  | anyOf(hasArgument(0, hasType(isSupportedSmartPointer())), | 
|  | hasArgument(1, hasType(isSupportedSmartPointer()))), | 
|  | // If one of the arguments isn't a smart pointer, it has to be | 
|  | // `std::nullptr_t`. | 
|  | hasArgument(0, anyOf(hasType(isSupportedSmartPointer()), | 
|  | hasType(isNullPtrType()))), | 
|  | hasArgument(1, anyOf(hasType(isSupportedSmartPointer()), | 
|  | hasType(isNullPtrType())))); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isSharedPtrCastCall() { | 
|  | return callExpr( | 
|  | argumentCountIs(1), | 
|  | hasArgument(0, hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl( | 
|  | isInStdNamespace(), hasName("shared_ptr")))))), | 
|  | callee(functionDecl( | 
|  | isInStdNamespace(), | 
|  | hasAnyName("static_pointer_cast", "dynamic_pointer_cast", | 
|  | "const_pointer_cast", "reinterpret_pointer_cast")))); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isWeakPtrLockCall() { | 
|  | return cxxMemberCallExpr( | 
|  | thisPointerType(cxxRecordDecl(isInStdNamespace(), hasName("weak_ptr"))), | 
|  | callee(cxxMethodDecl(hasName("lock")))); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isSupportedPointerAccessorCall() { | 
|  | return cxxMemberCallExpr(callee(cxxMethodDecl(hasBody(compoundStmt( | 
|  | statementCountIs(1), | 
|  | has(returnStmt(has(implicitCastExpr( | 
|  | hasCastKind(CK_LValueToRValue), | 
|  | has(ignoringParenImpCasts( | 
|  | memberExpr(has(ignoringParenImpCasts(cxxThisExpr())), | 
|  | hasType(isSupportedRawPointer()), | 
|  | hasDeclaration(decl().bind("member-decl")))))))))))))); | 
|  | } | 
|  |  | 
|  | Matcher<Stmt> isStatusOrValueOrCall() { | 
|  | return cxxMemberCallExpr( | 
|  | thisPointerType(qualType(hasCanonicalType(qualType( | 
|  | hasDeclaration(cxxRecordDecl(hasName("::absl::StatusOr"))))))), | 
|  | callee(cxxMethodDecl(hasName("value_or")))); | 
|  | } | 
|  |  | 
|  | }  // namespace clang::tidy::nullability |