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 RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()), unless(isExternC())) .bind(FunctionId), this); Finder->addMatcher(typedefNameDecl().bind(TypedefId), this); auto ParenFunctionType = parenType(innerType(functionType())); auto PointerToFunctionType = pointee(ParenFunctionType); auto FunctionOrMemberPointer = anyOf(hasType(pointerType(PointerToFunctionType)), hasType(memberPointerType(PointerToFunctionType))); Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(FieldId), this); Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId), this); auto CastDestinationIsFunction = hasDestinationType(pointsTo(ParenFunctionType)); Finder->addMatcher( cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId), this); Finder->addMatcher( cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId), this); Finder->addMatcher( cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId), this); Finder->addMatcher( cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId), this); Finder->addMatcher(lambdaExpr().bind(LambdaId), this); }
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); } }
TEST_F(StructuralEquivalenceRecordTest, UnnamedRecordsShouldBeInequivalentEvenIfTheSecondIsBeingDefined) { auto Code = R"( struct A { struct { struct A *next; } entry0; struct { struct A *next; } entry1; }; )"; auto t = makeTuDecls(Code, Code, Lang_C); auto *FromTU = get<0>(t); auto *Entry1 = FirstDeclMatcher<FieldDecl>().match(FromTU, fieldDecl(hasName("entry1"))); auto *ToTU = get<1>(t); auto *Entry0 = FirstDeclMatcher<FieldDecl>().match(ToTU, fieldDecl(hasName("entry0"))); auto *A = FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A"))); A->startDefinition(); // Set isBeingDefined, getDefinition() will return a // nullptr. This may be the case during ASTImport. auto *R0 = getRecordDecl(Entry0); auto *R1 = getRecordDecl(Entry1); ASSERT_NE(R0, R1); EXPECT_TRUE(testStructuralMatch(R0, R0)); EXPECT_TRUE(testStructuralMatch(R1, R1)); EXPECT_FALSE(testStructuralMatch(R0, R1)); }
void NonPrivateMemberVariablesInClassesCheck::registerMatchers( MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; // We can ignore structs/classes with all member variables being public. auto ShouldIgnoreRecord = allOf(boolean(IgnoreClassesWithAllMemberVariablesBeingPublic), unless(hasNonPublicMemberVariable())); // There are three visibility types: public, protected, private. // If we are ok with public fields, then we only want to complain about // protected fields, else we want to complain about all non-private fields. // We can ignore public member variables in structs/classes, in unions. auto InterestingField = fieldDecl( IgnorePublicMemberVariables ? isProtected() : unless(isPrivate())); // We only want the records that not only contain the mutable data (non-static // member variables), but also have some logic (non-static member functions). // We may optionally ignore records where all the member variables are public. Finder->addMatcher(cxxRecordDecl(anyOf(isStruct(), isClass()), hasMethods(), hasNonStaticMethod(), unless(ShouldIgnoreRecord), forEach(InterestingField.bind("field"))) .bind("record"), this); }
void StringReferenceMemberCheck::registerMatchers( ast_matchers::MatchFinder *Finder) { // Look for const references to std::string or ::string. auto String = anyOf(recordDecl(hasName("::std::basic_string")), recordDecl(hasName("::string"))); auto ConstString = qualType(isConstQualified(), hasDeclaration(String)); // Ignore members in template instantiations. Finder->addMatcher(fieldDecl(hasType(references(ConstString)), unless(isInstantiated())).bind("member"), 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); }
TEST_F(StructuralEquivalenceTest, WrongOrderOfFieldsInClass) { auto Code = "class X { int a; int b; };"; auto Decls = makeNamedDecls(Code, Code, Lang_CXX, "X"); CXXRecordDecl *RD = FirstDeclMatcher<CXXRecordDecl>().match( get<1>(Decls), cxxRecordDecl(hasName("X"))); FieldDecl *FD = FirstDeclMatcher<FieldDecl>().match(get<1>(Decls), fieldDecl(hasName("a"))); // Reorder the FieldDecls RD->removeDecl(FD); RD->addDeclInternal(FD); EXPECT_FALSE(testStructuralMatch(Decls)); }
TEST_F(StructuralEquivalenceRecordTest, Match) { auto Code = R"( struct A{ }; struct B{ }; struct foo: A, virtual B { void x(); int a; }; )"; auto t = makeNamedDecls(Code, Code, Lang_CXX); EXPECT_TRUE(testStructuralMatch(t)); } TEST_F(StructuralEquivalenceRecordTest, UnnamedRecordsShouldBeInequivalent) { auto t = makeTuDecls( R"( struct A { struct { struct A *next; } entry0; struct { struct A *next; } entry1; }; )", "", Lang_C); auto *TU = get<0>(t); auto *Entry0 = FirstDeclMatcher<FieldDecl>().match(TU, fieldDecl(hasName("entry0"))); auto *Entry1 = FirstDeclMatcher<FieldDecl>().match(TU, fieldDecl(hasName("entry1"))); auto *R0 = getRecordDecl(Entry0); auto *R1 = getRecordDecl(Entry1); ASSERT_NE(R0, R1); EXPECT_TRUE(testStructuralMatch(R0, R0)); EXPECT_TRUE(testStructuralMatch(R1, R1)); EXPECT_FALSE(testStructuralMatch(R0, R1)); }
void NonCopyableObjectsCheck::registerMatchers(MatchFinder *Finder) { // There are two ways to get into trouble with objects like FILE *: // dereferencing the pointer type to be a non-pointer type, and declaring // the type as a non-pointer type in the first place. While the declaration // itself could technically be well-formed in the case where the type is not // an opaque type, it's highly suspicious behavior. // // POSIX types are a bit different in that it's reasonable to declare a // non-pointer variable or data member of the type, but it is not reasonable // to dereference a pointer to the type, or declare a parameter of non-pointer // type. auto BadFILEType = hasType(namedDecl(isFILEType()).bind("type_decl")); auto BadPOSIXType = hasType(namedDecl(isPOSIXType()).bind("type_decl")); auto BadEitherType = anyOf(BadFILEType, BadPOSIXType); Finder->addMatcher( namedDecl(anyOf(varDecl(BadFILEType), fieldDecl(BadFILEType))) .bind("decl"), this); Finder->addMatcher(parmVarDecl(BadPOSIXType).bind("decl"), this); Finder->addMatcher( expr(unaryOperator(hasOperatorName("*"), BadEitherType)).bind("expr"), 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); }
namespace ProgrammingLanguage { //match sturct/union/class DeclarationMatcher recordFieldDeclMatcherPL = recordDecl(has(fieldDecl())).bind("recordDecl"); }