void StrCatAppendCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; const auto StrCat = functionDecl(hasName("::absl::StrCat")); // The arguments of absl::StrCat are implicitly converted to AlphaNum. This // matches to the arguments because of that behavior. const auto AlphaNum = IgnoringTemporaries(cxxConstructExpr( argumentCountIs(1), hasType(cxxRecordDecl(hasName("::absl::AlphaNum"))), hasArgument(0, ignoringImpCasts(declRefExpr(to(equalsBoundNode("LHS")), expr().bind("Arg0")))))); const auto HasAnotherReferenceToLhs = callExpr(hasAnyArgument(expr(hasDescendant(declRefExpr( to(equalsBoundNode("LHS")), unless(equalsBoundNode("Arg0"))))))); // Now look for calls to operator= with an object on the LHS and a call to // StrCat on the RHS. The first argument of the StrCat call should be the same // as the LHS. Ignore calls from template instantiations. Finder->addMatcher( cxxOperatorCallExpr( unless(isInTemplateInstantiation()), hasOverloadedOperatorName("="), hasArgument(0, declRefExpr(to(decl().bind("LHS")))), hasArgument(1, IgnoringTemporaries( callExpr(callee(StrCat), hasArgument(0, AlphaNum), unless(HasAnotherReferenceToLhs)) .bind("Call")))) .bind("Op"), this); }
void PassByValueCheck::registerMatchers(MatchFinder *Finder) { // Only register the matchers for C++; the functionality currently does not // provide any benefit to other languages, despite being benign. if (!getLangOpts().CPlusPlus) return; Finder->addMatcher( cxxConstructorDecl( forEachConstructorInitializer( cxxCtorInitializer( // Clang builds a CXXConstructExpr only whin it knows which // constructor will be called. In dependent contexts a // ParenListExpr is generated instead of a CXXConstructExpr, // filtering out templates automatically for us. withInitializer(cxxConstructExpr( has(ignoringParenImpCasts(declRefExpr(to( parmVarDecl( hasType(qualType( // Match only const-ref or a non-const value // parameters. Rvalues and const-values // shouldn't be modified. anyOf(constRefType(), nonConstValueType())))) .bind("Param"))))), hasDeclaration(cxxConstructorDecl( isCopyConstructor(), unless(isDeleted()), hasDeclContext( cxxRecordDecl(isMoveConstructible()))))))) .bind("Initializer"))) .bind("Ctor"), this); }
TEST_F(StructuralEquivalenceRecordTest, Name) { auto t = makeDecls<CXXRecordDecl>( "struct A{ };", "struct B{ };", Lang_CXX, cxxRecordDecl(hasName("A")), cxxRecordDecl(hasName("B"))); EXPECT_FALSE(testStructuralMatch(t)); }
void SlicingCheck::registerMatchers(MatchFinder *Finder) { // When we see: // class B : public A { ... }; // A a; // B b; // a = b; // The assignment is OK if: // - the assignment operator is defined as taking a B as second parameter, // or // - B does not define any additional members (either variables or // overrides) wrt A. // // The same holds for copy ctor calls. This also captures stuff like: // void f(A a); // f(b); // Helpers. const auto OfBaseClass = ofClass(cxxRecordDecl().bind("BaseDecl")); const auto IsDerivedFromBaseDecl = cxxRecordDecl(isDerivedFrom(equalsBoundNode("BaseDecl"))) .bind("DerivedDecl"); const auto HasTypeDerivedFromBaseDecl = anyOf(hasType(IsDerivedFromBaseDecl), hasType(references(IsDerivedFromBaseDecl))); const auto IsWithinDerivedCtor = hasParent(cxxConstructorDecl(ofClass(equalsBoundNode("DerivedDecl")))); // Assignement slicing: "a = b;" and "a = std::move(b);" variants. const auto SlicesObjectInAssignment = callExpr(callee(cxxMethodDecl(anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator()), OfBaseClass)), hasArgument(1, HasTypeDerivedFromBaseDecl)); // Construction slicing: "A a{b};" and "f(b);" variants. Note that in case of // slicing the letter will create a temporary and therefore call a ctor. const auto SlicesObjectInCtor = cxxConstructExpr( hasDeclaration(cxxConstructorDecl( anyOf(isCopyConstructor(), isMoveConstructor()), OfBaseClass)), hasArgument(0, HasTypeDerivedFromBaseDecl), // We need to disable matching on the call to the base copy/move // constructor in DerivedDecl's constructors. unless(IsWithinDerivedCtor)); Finder->addMatcher( expr(anyOf(SlicesObjectInAssignment, SlicesObjectInCtor)).bind("Call"), this); }
void UseNodiscardCheck::registerMatchers(MatchFinder *Finder) { // If we use ``[[nodiscard]]`` attribute, we require at least C++17. Use a // macro or ``__attribute__`` with pre c++17 compilers by using // ReplacementString option. if ((NoDiscardMacro == "[[nodiscard]]" && !getLangOpts().CPlusPlus17) || !getLangOpts().CPlusPlus) return; auto FunctionObj = cxxRecordDecl(hasAnyName("::std::function", "::boost::function")); // Find all non-void const methods which have not already been marked to // warn on unused result. Finder->addMatcher( cxxMethodDecl( allOf(isConst(), isDefinitionOrInline(), unless(anyOf( returns(voidType()), isNoReturn(), isOverloadedOperator(), isVariadic(), hasTemplateReturnType(), hasClassMutableFields(), isConversionOperator(), hasAttr(clang::attr::WarnUnusedResult), hasType(isInstantiationDependentType()), hasAnyParameter(anyOf( parmVarDecl(anyOf(hasType(FunctionObj), hasType(references(FunctionObj)))), hasType(isNonConstReferenceOrPointer()), hasParameterPack())))))) .bind("no_discard"), this); }
void UseEqualsDeleteCheck::registerMatchers(MatchFinder *Finder) { auto PrivateSpecialFn = cxxMethodDecl( isPrivate(), anyOf(cxxConstructorDecl(anyOf(isDefaultConstructor(), isCopyConstructor(), isMoveConstructor())), cxxMethodDecl( anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())), cxxDestructorDecl())); Finder->addMatcher( cxxMethodDecl( PrivateSpecialFn, unless(anyOf(hasBody(stmt()), isDefaulted(), isDeleted(), ast_matchers::isTemplateInstantiation(), // Ensure that all methods except private special member // functions are defined. hasParent(cxxRecordDecl(hasMethod(unless( anyOf(PrivateSpecialFn, hasBody(stmt()), isPure(), isDefaulted(), isDeleted())))))))) .bind(SpecialFunction), this); Finder->addMatcher( cxxMethodDecl(isDeleted(), unless(isPublic())).bind(DeletedNotPublic), this); }
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); }
TEST_F(StructuralEquivalenceRecordTest, TemplateVsNonTemplate) { auto t = makeDecls<CXXRecordDecl>( "struct A { };", "template<class T> struct A { };", Lang_CXX, cxxRecordDecl(hasName("A"))); EXPECT_FALSE(testStructuralMatch(t)); }
void ExceptionBaseclassCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; Finder->addMatcher( cxxThrowExpr(allOf(has(expr(unless(hasType(qualType(hasCanonicalType( hasDeclaration(cxxRecordDecl(isSameOrDerivedFrom( hasName("std::exception")))))))))), has(expr(unless(cxxUnresolvedConstructExpr()))), eachOf(has(expr(hasType(namedDecl().bind("decl")))), anything()))) .bind("bad_throw"), 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 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( cxxMemberCallExpr( on(expr().bind("left")), callee(memberExpr().bind("reset_member")), callee( cxxMethodDecl(hasName("reset"), ofClass(cxxRecordDecl(hasName("::std::unique_ptr"), decl().bind("left_class"))))), has(ignoringParenImpCasts(cxxMemberCallExpr( on(expr().bind("right")), callee(memberExpr().bind("release_member")), callee(cxxMethodDecl( hasName("release"), ofClass(cxxRecordDecl(hasName("::std::unique_ptr"), decl().bind("right_class"))))))))) .bind("reset_call"), this); }
void UndelegatedConstructorCheck::registerMatchers(MatchFinder *Finder) { // We look for calls to constructors of the same type in constructors. To do // this we have to look through a variety of nodes that occur in the path, // depending on the type's destructor and the number of arguments on the // constructor call, this is handled by ignoringTemporaryExpr. Ignore template // instantiations to reduce the number of duplicated warnings. // // 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( compoundStmt( hasParent( cxxConstructorDecl(ofClass(cxxRecordDecl().bind("parent")))), forEach(ignoringTemporaryExpr( cxxConstructExpr(hasDeclaration(cxxConstructorDecl(ofClass( cxxRecordDecl(baseOfBoundNode("parent")))))) .bind("construct"))), unless(isInTemplateInstantiation())), 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)); }
void TrailingReturnCheck::registerMatchers(MatchFinder *Finder) { // Requires C++11 or later. if (!getLangOpts().CPlusPlus11) return; // Functions that have trailing returns are disallowed, except for those // using decltype specifiers and lambda with otherwise unutterable // return types. Finder->addMatcher( functionDecl(allOf(hasTrailingReturn(), unless(anyOf(returns(decltypeType()), hasParent(cxxRecordDecl(isLambda())))))) .bind("decl"), this); }
void SizeofContainerCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( expr(unless(isInTemplateInstantiation()), expr(sizeOfExpr(has(ignoringParenImpCasts( expr(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl( matchesName("^(::std::|::string)"), unless(matchesName("^::std::(bitset|array)$")), hasMethod(cxxMethodDecl(hasName("size"), isPublic(), isConst()))))))))))) .bind("sizeof"), // Ignore ARRAYSIZE(<array of containers>) pattern. unless(hasAncestor(binaryOperator( anyOf(hasOperatorName("/"), hasOperatorName("%")), hasLHS(ignoringParenCasts(sizeOfExpr(expr()))), hasRHS(ignoringParenCasts(equalsBoundNode("sizeof"))))))), this); }
void AvoidConstParamsInDecls::registerMatchers(MatchFinder *Finder) { const auto ConstParamDecl = parmVarDecl(hasType(qualType(isConstQualified()))).bind("param"); Finder->addMatcher( functionDecl(unless(isDefinition()), // Lambdas are always their own definition, but they // generate a non-definition FunctionDecl too. Ignore those. // Class template instantiations have a non-definition // CXXMethodDecl for methods that aren't used in this // translation unit. Ignore those, as the template will have // already been checked. unless(cxxMethodDecl(ofClass(cxxRecordDecl(anyOf( isLambda(), ast_matchers::isTemplateInstantiation()))))), has(typeLoc(forEach(ConstParamDecl)))) .bind("func"), this); }
void MoveConstructorInitCheck::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( cxxConstructorDecl( unless(isImplicit()), allOf(isMoveConstructor(), hasAnyConstructorInitializer( cxxCtorInitializer( withInitializer(cxxConstructExpr(hasDeclaration( cxxConstructorDecl(isCopyConstructor()) .bind("ctor"))))) .bind("move-init")))), this); auto NonConstValueMovableAndExpensiveToCopy = qualType(allOf(unless(pointerType()), unless(isConstQualified()), hasDeclaration(cxxRecordDecl(hasMethod(cxxConstructorDecl( isMoveConstructor(), unless(isDeleted()))))), matchers::isExpensiveToCopy())); // This checker is also used to implement cert-oop11-cpp, but when using that // form of the checker, we do not want to diagnose movable parameters. if (!UseCERTSemantics) { Finder->addMatcher( cxxConstructorDecl( allOf( unless(isMoveConstructor()), hasAnyConstructorInitializer(withInitializer(cxxConstructExpr( hasDeclaration(cxxConstructorDecl(isCopyConstructor())), hasArgument( 0, declRefExpr( to(parmVarDecl( hasType( NonConstValueMovableAndExpensiveToCopy)) .bind("movable-param"))) .bind("init-arg"))))))) .bind("ctor-decl"), this); } }
void UniqueptrDeleteReleaseCheck::registerMatchers(MatchFinder *Finder) { auto IsSusbstituted = qualType(anyOf( substTemplateTypeParmType(), hasDescendant(substTemplateTypeParmType()))); auto UniquePtrWithDefaultDelete = classTemplateSpecializationDecl( hasName("std::unique_ptr"), hasTemplateArgument(1, refersToType(qualType(hasDeclaration(cxxRecordDecl( hasName("std::default_delete"))))))); Finder->addMatcher( cxxDeleteExpr(has(ignoringParenImpCasts(cxxMemberCallExpr( on(expr(hasType(UniquePtrWithDefaultDelete), unless(hasType(IsSusbstituted))) .bind("uptr")), callee(cxxMethodDecl(hasName("release"))))))) .bind("delete"), 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 StaticObjectExceptionCheck::registerMatchers(MatchFinder *Finder) { if ((!getLangOpts().CPlusPlus) || (!getLangOpts().CXXExceptions)) return; // Match any static or thread_local variable declaration that has an // initializer that can throw. Finder->addMatcher( varDecl(anyOf(hasThreadStorageDuration(), hasStaticStorageDuration()), unless(anyOf(isConstexpr(), hasType(cxxRecordDecl(isLambda())), hasAncestor(functionDecl()))), anyOf(hasDescendant(cxxConstructExpr(hasDeclaration( cxxConstructorDecl(unless(isNoThrow())).bind("func")))), hasDescendant(cxxNewExpr(hasDeclaration( functionDecl(unless(isNoThrow())).bind("func")))), hasDescendant(callExpr(hasDeclaration( functionDecl(unless(isNoThrow())).bind("func")))))) .bind("var"), this); }
void ProBoundsConstantArrayIndexCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; Finder->addMatcher(arraySubscriptExpr(hasBase(ignoringImpCasts(hasType( constantArrayType().bind("type")))), hasIndex(expr().bind("index"))) .bind("expr"), this); Finder->addMatcher( cxxOperatorCallExpr( hasOverloadedOperatorName("[]"), hasArgument( 0, hasType(cxxRecordDecl(hasName("::std::array")).bind("type"))), hasArgument(1, expr().bind("index"))) .bind("expr"), this); }
void RedundantMemberInitCheck::registerMatchers(MatchFinder *Finder) { auto Construct = cxxConstructExpr( hasDeclaration(cxxConstructorDecl(hasParent( cxxRecordDecl(unless(isTriviallyDefaultConstructible())))))) .bind("construct"); Finder->addMatcher( cxxConstructorDecl( unless(isDelegatingConstructor()), ofClass(unless( anyOf(isUnion(), ast_matchers::isTemplateInstantiation()))), forEachConstructorInitializer( cxxCtorInitializer(isWritten(), withInitializer(ignoringImplicit(Construct)), unless(forField(hasType(isConstQualified())))) .bind("init"))), this); }
void SpecialMemberFunctionsCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; Finder->addMatcher( cxxRecordDecl( eachOf( has(cxxDestructorDecl(unless(isImplicit())).bind("dtor")), has(cxxConstructorDecl(isCopyConstructor(), unless(isImplicit())) .bind("copy-ctor")), has(cxxMethodDecl(isCopyAssignmentOperator(), unless(isImplicit())) .bind("copy-assign")), has(cxxConstructorDecl(isMoveConstructor(), unless(isImplicit())) .bind("move-ctor")), has(cxxMethodDecl(isMoveAssignmentOperator(), unless(isImplicit())) .bind("move-assign")))) .bind("class-def"), this); }
void InefficientStringConcatenationCheck::registerMatchers( MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; const auto BasicStringType = hasType(qualType(hasUnqualifiedDesugaredType(recordType( hasDeclaration(cxxRecordDecl(hasName("::std::basic_string"))))))); const auto BasicStringPlusOperator = cxxOperatorCallExpr( hasOverloadedOperatorName("+"), hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType)))); const auto PlusOperator = cxxOperatorCallExpr( hasOverloadedOperatorName("+"), hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType))), hasDescendant(BasicStringPlusOperator)) .bind("plusOperator"); const auto AssignOperator = cxxOperatorCallExpr( hasOverloadedOperatorName("="), hasArgument(0, declRefExpr(BasicStringType, hasDeclaration(decl().bind("lhsStrT"))) .bind("lhsStr")), hasArgument(1, stmt(hasDescendant(declRefExpr( hasDeclaration(decl(equalsBoundNode("lhsStrT"))))))), hasDescendant(BasicStringPlusOperator)); if (StrictMode) { Finder->addMatcher(cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator)), this); } else { Finder->addMatcher( cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator), hasAncestor(stmt(anyOf(cxxForRangeStmt(), whileStmt(), forStmt())))), this); } }
void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) { // Only register the matchers for C++; the functionality currently does not // provide any benefit to other languages, despite being benign. if (!getLangOpts().CPlusPlus) return; const auto ValidContainer = cxxRecordDecl(isSameOrDerivedFrom( namedDecl( has(cxxMethodDecl( isConst(), parameterCountIs(0), isPublic(), hasName("size"), returns(qualType(isInteger(), unless(booleanType())))) .bind("size")), has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(), hasName("empty"), returns(booleanType())) .bind("empty"))) .bind("container"))); const auto WrongUse = anyOf( hasParent(binaryOperator( matchers::isComparisonOperator(), hasEitherOperand(ignoringImpCasts(anyOf( integerLiteral(equals(1)), integerLiteral(equals(0)))))) .bind("SizeBinaryOp")), hasParent(implicitCastExpr( hasImplicitDestinationType(booleanType()), anyOf( hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")), anything()))), hasParent(explicitCastExpr(hasDestinationType(booleanType())))); Finder->addMatcher( cxxMemberCallExpr(on(expr(anyOf(hasType(ValidContainer), hasType(pointsTo(ValidContainer)), hasType(references(ValidContainer)))) .bind("STLObject")), callee(cxxMethodDecl(hasName("size"))), WrongUse) .bind("SizeCallExpr"), 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 InheritanceBuilder::tackOnto(MatchFinder &MF) { MF.addMatcher(cxxRecordDecl(isDefinition(), unless(hasAncestor(namespaceDecl(isAnonymous())))) .bind("class"), this); }
void CanRunScriptChecker::registerMatchers(MatchFinder *AstMatcher) { auto InvalidArg = // We want to find any expression, ignoreTrivials(expr( // which has a refcounted pointer type, hasType(pointerType( pointee(hasDeclaration(cxxRecordDecl(isRefCounted()))))), // and which is not this, unless(cxxThisExpr()), // and which is not a method call on a smart ptr, unless(cxxMemberCallExpr(on(hasType(isSmartPtrToRefCounted())))), // and which is not a parameter of the parent function, unless(declRefExpr(to(parmVarDecl()))), // and which is not a MOZ_KnownLive wrapped value. unless(callExpr(callee(functionDecl(hasName("MOZ_KnownLive"))))), expr().bind("invalidArg"))); auto OptionalInvalidExplicitArg = anyOf( // We want to find any argument which is invalid. hasAnyArgument(InvalidArg), // This makes this matcher optional. anything()); // Please not that the hasCanRunScriptAnnotation() matchers are not present // directly in the cxxMemberCallExpr, callExpr and constructExpr matchers // because we check that the corresponding functions can run script later in // the checker code. AstMatcher->addMatcher( expr( anyOf( // We want to match a method call expression, cxxMemberCallExpr( // which optionally has an invalid arg, OptionalInvalidExplicitArg, // or which optionally has an invalid implicit this argument, anyOf( // which derefs into an invalid arg, on(cxxOperatorCallExpr( anyOf(hasAnyArgument(InvalidArg), anything()))), // or is an invalid arg. on(InvalidArg), anything()), expr().bind("callExpr")), // or a regular call expression, callExpr( // which optionally has an invalid arg. OptionalInvalidExplicitArg, expr().bind("callExpr")), // or a construct expression, cxxConstructExpr( // which optionally has an invalid arg. OptionalInvalidExplicitArg, expr().bind("constructExpr"))), anyOf( // We want to match the parent function. forFunction(functionDecl().bind("nonCanRunScriptParentFunction")), // ... optionally. anything())), this); }
void UpgradeDurationConversionsCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; // For the arithmetic calls, we match only the uses of the templated operators // where the template parameter is not a built-in type. This means the // instantiation makes use of an available user defined conversion to // `int64_t`. // // The implementation of these templates will be updated to fail SFINAE for // non-integral types. We match them to suggest an explicit cast. // Match expressions like `a *= b` and `a /= b` where `a` has type // `absl::Duration` and `b` is not of a built-in type. Finder->addMatcher( cxxOperatorCallExpr( argumentCountIs(2), hasArgument( 0, expr(hasType(cxxRecordDecl(hasName("::absl::Duration"))))), hasArgument(1, expr().bind("arg")), callee(functionDecl( hasParent(functionTemplateDecl()), unless(hasTemplateArgument(0, refersToType(builtinType()))), hasAnyName("operator*=", "operator/=")))), this); // Match expressions like `a.operator*=(b)` and `a.operator/=(b)` where `a` // has type `absl::Duration` and `b` is not of a built-in type. Finder->addMatcher( cxxMemberCallExpr( callee(cxxMethodDecl( ofClass(cxxRecordDecl(hasName("::absl::Duration"))), hasParent(functionTemplateDecl()), unless(hasTemplateArgument(0, refersToType(builtinType()))), hasAnyName("operator*=", "operator/="))), argumentCountIs(1), hasArgument(0, expr().bind("arg"))), this); // Match expressions like `a * b`, `a / b`, `operator*(a, b)`, and // `operator/(a, b)` where `a` has type `absl::Duration` and `b` is not of a // built-in type. Finder->addMatcher( callExpr(callee(functionDecl( hasParent(functionTemplateDecl()), unless(hasTemplateArgument(0, refersToType(builtinType()))), hasAnyName("::absl::operator*", "::absl::operator/"))), argumentCountIs(2), hasArgument(0, expr(hasType( cxxRecordDecl(hasName("::absl::Duration"))))), hasArgument(1, expr().bind("arg"))), this); // Match expressions like `a * b` and `operator*(a, b)` where `a` is not of a // built-in type and `b` has type `absl::Duration`. Finder->addMatcher( callExpr(callee(functionDecl( hasParent(functionTemplateDecl()), unless(hasTemplateArgument(0, refersToType(builtinType()))), hasName("::absl::operator*"))), argumentCountIs(2), hasArgument(0, expr().bind("arg")), hasArgument(1, expr(hasType(cxxRecordDecl( hasName("::absl::Duration")))))), this); // For the factory functions, we match only the non-templated overloads that // take an `int64_t` parameter. Within these calls, we care about implicit // casts through a user defined conversion to `int64_t`. // // The factory functions will be updated to be templated and SFINAE on whether // the template parameter is an integral type. This complements the already // existing templated overloads that only accept floating point types. // Match calls like: // `absl::Nanoseconds(x)` // `absl::Microseconds(x)` // `absl::Milliseconds(x)` // `absl::Seconds(x)` // `absl::Minutes(x)` // `absl::Hours(x)` // where `x` is not of a built-in type. Finder->addMatcher( implicitCastExpr( anyOf(hasCastKind(CK_UserDefinedConversion), has(implicitCastExpr(hasCastKind(CK_UserDefinedConversion)))), hasParent(callExpr( callee(functionDecl(DurationFactoryFunction(), unless(hasParent(functionTemplateDecl())))), hasArgument(0, expr().bind("arg"))))), this); }
void OverrideBaseCallChecker::registerMatchers(MatchFinder* AstMatcher) { AstMatcher->addMatcher(cxxRecordDecl(hasBaseClasses()).bind("class"), this); }