void DeleteNullPointerCheck::registerMatchers(MatchFinder *Finder) { const auto DeleteExpr = cxxDeleteExpr(has(castExpr(has(declRefExpr( to(decl(equalsBoundNode("deletedPointer")))))))) .bind("deleteExpr"); const auto DeleteMemberExpr = cxxDeleteExpr(has(castExpr(has(memberExpr(hasDeclaration( fieldDecl(equalsBoundNode("deletedMemberPointer")))))))) .bind("deleteMemberExpr"); const auto PointerExpr = ignoringImpCasts(anyOf( declRefExpr(to(decl().bind("deletedPointer"))), memberExpr(hasDeclaration(fieldDecl().bind("deletedMemberPointer"))))); const auto PointerCondition = castExpr(hasCastKind(CK_PointerToBoolean), hasSourceExpression(PointerExpr)); const auto BinaryPointerCheckCondition = binaryOperator(hasEitherOperand(castExpr(hasCastKind(CK_NullToPointer))), hasEitherOperand(PointerExpr)); Finder->addMatcher( ifStmt(hasCondition(anyOf(PointerCondition, BinaryPointerCheckCondition)), hasThen(anyOf( DeleteExpr, DeleteMemberExpr, compoundStmt(anyOf(has(DeleteExpr), has(DeleteMemberExpr)), statementCountIs(1)) .bind("compound")))) .bind("ifWithDelete"), this); }
void ShrinkToFitCheck::registerMatchers(MatchFinder *Finder) { // Swap as a function need not to be considered, because rvalue can not // be bound to a non-const reference. const auto ShrinkableAsMember = memberExpr(member(valueDecl().bind("ContainerDecl"))); const auto ShrinkableAsDecl = declRefExpr(hasDeclaration(valueDecl().bind("ContainerDecl"))); const auto CopyCtorCall = constructExpr( hasArgument(0, anyOf(ShrinkableAsMember, ShrinkableAsDecl, unaryOperator(has(ShrinkableAsMember)), unaryOperator(has(ShrinkableAsDecl))))); const auto SwapParam = expr(anyOf( memberExpr(member(equalsBoundNode("ContainerDecl"))), declRefExpr(hasDeclaration(equalsBoundNode("ContainerDecl"))), unaryOperator(has(memberExpr(member(equalsBoundNode("ContainerDecl"))))), unaryOperator( has(declRefExpr(hasDeclaration(equalsBoundNode("ContainerDecl"))))))); Finder->addMatcher( memberCallExpr(on(hasType(namedDecl(stlShrinkableContainer()))), callee(methodDecl(hasName("swap"))), has(memberExpr(hasDescendant(CopyCtorCall))), hasArgument(0, SwapParam.bind("ContainerToShrink")), unless(isInTemplateInstantiation())) .bind("CopyAndSwapTrick"), this); }
TEST(MemberExpr, ImplicitMemberRange) { RangeVerifier<MemberExpr> Verifier; Verifier.expectRange(2, 30, 2, 30); EXPECT_TRUE(Verifier.match("struct S { operator int() const; };\n" "int foo(const S& s) { return s; }", memberExpr())); }
void MultiwayPathsCoveredCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( switchStmt( hasCondition(allOf( // Match on switch statements that have either a bit-field or // an integer condition. The ordering in 'anyOf()' is // important because the last condition is the most general. anyOf(ignoringImpCasts(memberExpr(hasDeclaration( fieldDecl(isBitField()).bind("bitfield")))), ignoringImpCasts(declRefExpr().bind("non-enum-condition"))), // 'unless()' must be the last match here and must be bound, // otherwise the matcher does not work correctly, because it // will not explicitly ignore enum conditions. unless(ignoringImpCasts( declRefExpr(hasType(enumType())).bind("enum-condition")))))) .bind("switch"), this); // This option is noisy, therefore matching is configurable. if (WarnOnMissingElse) { Finder->addMatcher( ifStmt(allOf(hasParent(ifStmt()), unless(hasElse(anything())))) .bind("else-if"), this); } }
void StaticAccessedThroughInstanceCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( memberExpr(hasDeclaration(anyOf(cxxMethodDecl(isStaticStorageClass()), varDecl(hasStaticStorageDuration()))), unless(isInTemplateInstantiation())) .bind("memberExpression"), this); }
void ProTypeUnionAccessCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; Finder->addMatcher( memberExpr(hasObjectExpression(hasType(recordDecl(isUnion())))) .bind("expr"), this); }
void ParentVirtualCallCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( cxxMemberCallExpr( callee(memberExpr(hasDescendant(implicitCastExpr( hasImplicitDestinationType(pointsTo( type(anything()).bind("castToType"))), hasSourceExpression(cxxThisExpr(hasType( type(anything()).bind("thisType"))))))) .bind("member")), callee(cxxMethodDecl(isVirtual()))), this); }
void UniqueptrResetReleaseCheck::registerMatchers(MatchFinder *Finder) { // Only register the matchers for C++11; the functionality currently does not // provide any benefit to other languages, despite being benign. if (!getLangOpts().CPlusPlus11) return; Finder->addMatcher( memberCallExpr( on(expr().bind("left")), callee(memberExpr().bind("reset_member")), callee(methodDecl(hasName("reset"), ofClass(recordDecl(hasName("::std::unique_ptr"), decl().bind("left_class"))))), has(memberCallExpr( on(expr().bind("right")), callee(memberExpr().bind("release_member")), callee(methodDecl( hasName("release"), ofClass(recordDecl(hasName("::std::unique_ptr"), decl().bind("right_class")))))))) .bind("reset_call"), this); }
void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus11) return; // FIXME: Bunch of functionality that could be easily added: // + add handling of `push_front` for std::forward_list, std::list // and std::deque. // + add handling of `push` for std::stack, std::queue, std::priority_queue // + add handling of `insert` for stl associative container, but be careful // because this requires special treatment (it could cause performance // regression) // + match for emplace calls that should be replaced with insertion // + match for make_pair calls. auto callPushBack = cxxMemberCallExpr( hasDeclaration(functionDecl(hasName("push_back"))), on(hasType(cxxRecordDecl(hasAnyName("std::vector", "llvm::SmallVector", "std::list", "std::deque"))))); // We can't replace push_backs of smart pointer because // if emplacement fails (f.e. bad_alloc in vector) we will have leak of // passed pointer because smart pointer won't be constructed // (and destructed) as in push_back case. auto isCtorOfSmartPtr = hasDeclaration(cxxConstructorDecl( ofClass(hasAnyName("std::shared_ptr", "std::unique_ptr", "std::auto_ptr", "std::weak_ptr")))); // Bitfields binds only to consts and emplace_back take it by universal ref. auto bitFieldAsArgument = hasAnyArgument(ignoringParenImpCasts( memberExpr(hasDeclaration(fieldDecl(matchers::isBitfield()))))); // We could have leak of resource. auto newExprAsArgument = hasAnyArgument(ignoringParenImpCasts(cxxNewExpr())); auto constructingDerived = hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase))); auto hasInitList = has(ignoringParenImpCasts(initListExpr())); auto soughtConstructExpr = cxxConstructExpr( unless(anyOf(isCtorOfSmartPtr, hasInitList, bitFieldAsArgument, newExprAsArgument, constructingDerived, has(materializeTemporaryExpr(hasInitList))))) .bind("ctor"); auto hasConstructExpr = has(ignoringParenImpCasts(soughtConstructExpr)); auto ctorAsArgument = materializeTemporaryExpr( anyOf(hasConstructExpr, has(cxxFunctionalCastExpr(hasConstructExpr)))); Finder->addMatcher( cxxMemberCallExpr(callPushBack, has(ctorAsArgument)).bind("call"), this); }
void ProperlySeededRandomGeneratorCheck::registerMatchers(MatchFinder *Finder) { auto RandomGeneratorEngineDecl = cxxRecordDecl(hasAnyName( "::std::linear_congruential_engine", "::std::mersenne_twister_engine", "::std::subtract_with_carry_engine", "::std::discard_block_engine", "::std::independent_bits_engine", "::std::shuffle_order_engine")); auto RandomGeneratorEngineTypeMatcher = hasType(hasUnqualifiedDesugaredType( recordType(hasDeclaration(RandomGeneratorEngineDecl)))); // std::mt19937 engine; // engine.seed(); // ^ // engine.seed(1); // ^ // const int x = 1; // engine.seed(x); // ^ Finder->addMatcher( cxxMemberCallExpr( has(memberExpr(has(declRefExpr(RandomGeneratorEngineTypeMatcher)), member(hasName("seed")), unless(hasDescendant(cxxThisExpr()))))) .bind("seed"), this); // std::mt19937 engine; // ^ // std::mt19937 engine(1); // ^ // const int x = 1; // std::mt19937 engine(x); // ^ Finder->addMatcher( cxxConstructExpr(RandomGeneratorEngineTypeMatcher).bind("ctor"), this); // srand(); // ^ // const int x = 1; // srand(x); // ^ Finder->addMatcher( callExpr(callee(functionDecl(hasAnyName("::srand", "::std::srand")))) .bind("srand"), this); }
void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus11) return; // FIXME: Bunch of functionality that could be easily added: // + add handling of `push_front` for std::forward_list, std::list // and std::deque. // + add handling of `push` for std::stack, std::queue, std::priority_queue // + add handling of `insert` for stl associative container, but be careful // because this requires special treatment (it could cause performance // regression) // + match for emplace calls that should be replaced with insertion // + match for make_pair calls. auto callPushBack = cxxMemberCallExpr( hasDeclaration(functionDecl(hasName("push_back"))), on(hasType(cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>( ContainersWithPushBack.begin(), ContainersWithPushBack.end())))))); // We can't replace push_backs of smart pointer because // if emplacement fails (f.e. bad_alloc in vector) we will have leak of // passed pointer because smart pointer won't be constructed // (and destructed) as in push_back case. auto isCtorOfSmartPtr = hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName( SmallVector<StringRef, 5>(SmartPointers.begin(), SmartPointers.end()))))); // Bitfields binds only to consts and emplace_back take it by universal ref. auto bitFieldAsArgument = hasAnyArgument( ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField()))))); // Initializer list can't be passed to universal reference. auto initializerListAsArgument = hasAnyArgument( ignoringImplicit(cxxConstructExpr(isListInitialization()))); // We could have leak of resource. auto newExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr())); // We would call another constructor. auto constructingDerived = hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase))); // emplace_back can't access private constructor. auto isPrivateCtor = hasDeclaration(cxxConstructorDecl(isPrivate())); auto hasInitList = has(ignoringImplicit(initListExpr())); // FIXME: Discard 0/NULL (as nullptr), static inline const data members, // overloaded functions and template names. auto soughtConstructExpr = cxxConstructExpr( unless(anyOf(isCtorOfSmartPtr, hasInitList, bitFieldAsArgument, initializerListAsArgument, newExprAsArgument, constructingDerived, isPrivateCtor))) .bind("ctor"); auto hasConstructExpr = has(ignoringImplicit(soughtConstructExpr)); auto ctorAsArgument = materializeTemporaryExpr( anyOf(hasConstructExpr, has(cxxFunctionalCastExpr(hasConstructExpr)))); Finder->addMatcher(cxxMemberCallExpr(callPushBack, has(ctorAsArgument), unless(isInTemplateInstantiation())) .bind("call"), this); }
void NoAddRefReleaseOnReturnChecker::registerMatchers(MatchFinder *AstMatcher) { // Look for all of the calls to AddRef() or Release() AstMatcher->addMatcher( memberExpr(isAddRefOrRelease(), hasParent(callExpr())).bind("member"), this); }