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); }
/// \brief Creates a matcher that finds the \c std::auto_ptr copy-ctor and /// assign-operator expressions. /// /// \c AutoPtrOwnershipTransferId is assigned to the argument of the expression, /// this is the part that has to be wrapped by \c std::move(). /// /// \code /// std::auto_ptr<int> i, j; /// i = j; /// ~~~~^ /// \endcode StatementMatcher makeTransferOwnershipExprMatcher() { return anyOf( cxxOperatorCallExpr(allOf(hasOverloadedOperatorName("="), callee(cxxMethodDecl(ofClass(AutoPtrDecl))), hasArgument(1, MovableArgumentMatcher))), cxxConstructExpr(allOf(hasType(AutoPtrType), argumentCountIs(1), hasArgument(0, MovableArgumentMatcher)))); }
TEST_F(StructuralEquivalenceCXXMethodTest, Operator) { auto t = makeDecls<FunctionDecl>( "struct X { int operator +(int); };", "struct X { int operator -(int); };", Lang_CXX, functionDecl(hasOverloadedOperatorName("+")), functionDecl(hasOverloadedOperatorName("-"))); EXPECT_FALSE(testStructuralMatch(t)); }
void OverloadedUnaryAndCheck::registerMatchers(ast_matchers::MatchFinder *Finder) { // Match unary methods that overload operator&. Finder->addMatcher(methodDecl(parameterCountIs(0), hasOverloadedOperatorName( "&")).bind("overload"), this); // Also match freestanding unary operator& overloads. Be careful not to match // binary methods. Finder->addMatcher( functionDecl( allOf(unless(methodDecl()), functionDecl(parameterCountIs(1), hasOverloadedOperatorName("&")).bind("overload"))), this); }
void NoexceptMoveConstructorCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( methodDecl(anyOf(constructorDecl(), hasOverloadedOperatorName("=")), unless(isImplicit()), unless(isDeleted())) .bind("decl"), this); }
void StringIntegerAssignmentCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; Finder->addMatcher( cxxOperatorCallExpr( anyOf(hasOverloadedOperatorName("="), hasOverloadedOperatorName("+=")), callee(cxxMethodDecl(ofClass(classTemplateSpecializationDecl( hasName("::std::basic_string"), hasTemplateArgument(0, refersToType(qualType().bind("type"))))))), hasArgument(1, ignoringImpCasts(expr(hasType(isInteger()), unless(hasType(isAnyCharacter()))) .bind("expr"))), unless(isInTemplateInstantiation())), this); }
void OverloadedUnaryAndCheck::registerMatchers( ast_matchers::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; // Match unary methods that overload operator&. Finder->addMatcher( cxxMethodDecl(parameterCountIs(0), hasOverloadedOperatorName("&")) .bind("overload"), this); // Also match freestanding unary operator& overloads. Be careful not to match // binary methods. Finder->addMatcher(functionDecl(unless(cxxMethodDecl()), parameterCountIs(1), hasOverloadedOperatorName("&")) .bind("overload"), this); }
void NoexceptMoveConstructorCheck::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( cxxMethodDecl(anyOf(cxxConstructorDecl(), hasOverloadedOperatorName("=")), unless(isImplicit()), unless(isDeleted())) .bind("decl"), 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 NewDeleteOverloadsCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; // Match all operator new and operator delete overloads (including the array // forms). Do not match implicit operators, placement operators, or // deleted/private operators. // // Technically, trivially-defined operator delete seems like a reasonable // thing to also skip. e.g., void operator delete(void *) {} // However, I think it's more reasonable to warn in this case as the user // should really be writing that as a deleted function. Finder->addMatcher( functionDecl(unless(anyOf(isImplicit(), isPlacementOverload(), isDeleted(), cxxMethodDecl(isPrivate()))), anyOf(hasOverloadedOperatorName("new"), hasOverloadedOperatorName("new[]"), hasOverloadedOperatorName("delete"), hasOverloadedOperatorName("delete[]"))) .bind("func"), this); }
void ReplaceAutoPtrCheck::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; auto AutoPtrDecl = recordDecl(hasName("auto_ptr"), isFromStdNamespace()); auto AutoPtrType = qualType(hasDeclaration(AutoPtrDecl)); // std::auto_ptr<int> a; // ^~~~~~~~~~~~~ // // typedef std::auto_ptr<int> int_ptr_t; // ^~~~~~~~~~~~~ // // std::auto_ptr<int> fn(std::auto_ptr<int>); // ^~~~~~~~~~~~~ ^~~~~~~~~~~~~ Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType, // Skip elaboratedType() as the named // type will match soon thereafter. unless(elaboratedType())))) .bind(AutoPtrTokenId), this); // using std::auto_ptr; // ^~~~~~~~~~~~~~~~~~~ Finder->addMatcher(usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(allOf( hasName("auto_ptr"), isFromStdNamespace())))) .bind(AutoPtrTokenId), this); // Find ownership transfers via copy construction and assignment. // AutoPtrOwnershipTransferId is bound to the the part that has to be wrapped // into std::move(). // std::auto_ptr<int> i, j; // i = j; // ~~~~^ auto MovableArgumentMatcher = expr(isLValue(), hasType(AutoPtrType)).bind(AutoPtrOwnershipTransferId); Finder->addMatcher( cxxOperatorCallExpr(hasOverloadedOperatorName("="), callee(cxxMethodDecl(ofClass(AutoPtrDecl))), hasArgument(1, MovableArgumentMatcher)), this); Finder->addMatcher(cxxConstructExpr(hasType(AutoPtrType), argumentCountIs(1), hasArgument(0, MovableArgumentMatcher)), 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 CanRunScriptChecker::registerMatchers(MatchFinder *AstMatcher) { auto Refcounted = qualType(hasDeclaration(cxxRecordDecl(isRefCounted()))); auto InvalidArg = // We want to find any expression, ignoreTrivials(expr( // which has a refcounted pointer type, anyOf( hasType(Refcounted), hasType(pointsTo(Refcounted)), hasType(references(Refcounted)) ), // 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 calling operator* on a smart ptr. unless( allOf( cxxOperatorCallExpr(hasOverloadedOperatorName("*")), callExpr(allOf( hasAnyArgument(hasType(isSmartPtrToRefCounted())), argumentCountIs(1) )) ) ), // 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 note 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); }