/// \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)))); }
void IncorrectRoundings::registerMatchers(MatchFinder *MatchFinder) { // Match a floating literal with value 0.5. auto FloatHalf = floatLiteral(floatHalf()); // Match a floating point expression. auto FloatType = expr(hasType(realFloatingPointType())); // Match a floating literal of 0.5 or a floating literal of 0.5 implicitly. // cast to floating type. auto FloatOrCastHalf = anyOf(FloatHalf, implicitCastExpr(FloatType, has(FloatHalf))); // Match if either the LHS or RHS is a floating literal of 0.5 or a floating // literal of 0.5 and the other is of type double or vice versa. auto OneSideHalf = anyOf(allOf(hasLHS(FloatOrCastHalf), hasRHS(FloatType)), allOf(hasRHS(FloatOrCastHalf), hasLHS(FloatType))); // Find expressions of cast to int of the sum of a floating point expression // and 0.5. MatchFinder->addMatcher( implicitCastExpr( hasImplicitDestinationType(isInteger()), ignoringParenCasts(binaryOperator(hasOperatorName("+"), OneSideHalf))) .bind("CastExpr"), this); }
void UseUncaughtExceptionsCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus17) return; std::string MatchText = "::std::uncaught_exception"; // Using declaration: warning and fix-it. Finder->addMatcher( usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(hasName(MatchText)))) .bind("using_decl"), this); // DeclRefExpr: warning, no fix-it. Finder->addMatcher(declRefExpr(allOf(to(functionDecl(hasName(MatchText))), unless(callExpr()))) .bind("decl_ref_expr"), this); // CallExpr: warning, fix-it. Finder->addMatcher( callExpr(allOf(hasDeclaration(functionDecl(hasName(MatchText))), unless(hasAncestor(initListExpr())))) .bind("call_expr"), this); // CallExpr in initialisation list: warning, fix-it with avoiding narrowing // conversions. Finder->addMatcher( callExpr(allOf(hasAncestor(initListExpr()), hasDeclaration(functionDecl(hasName(MatchText))))) .bind("init_call_expr"), this); }
void SuspiciousEnumUsageCheck::registerMatchers(MatchFinder *Finder) { const auto enumExpr = [](StringRef RefName, StringRef DeclName) { return allOf(ignoringImpCasts(expr().bind(RefName)), ignoringImpCasts(hasType(enumDecl().bind(DeclName)))); }; Finder->addMatcher( binaryOperator(hasOperatorName("|"), hasLHS(enumExpr("", "enumDecl")), hasRHS(allOf(enumExpr("", "otherEnumDecl"), ignoringImpCasts(hasType(enumDecl( unless(equalsBoundNode("enumDecl")))))))) .bind("diffEnumOp"), this); Finder->addMatcher( binaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("|")), hasLHS(enumExpr("lhsExpr", "enumDecl")), hasRHS(allOf(enumExpr("rhsExpr", ""), ignoringImpCasts(hasType(enumDecl( equalsBoundNode("enumDecl"))))))), this); Finder->addMatcher( binaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("|")), hasEitherOperand( allOf(hasType(isInteger()), unless(enumExpr("", "")))), hasEitherOperand(enumExpr("enumExpr", "enumDecl"))), this); Finder->addMatcher( binaryOperator(anyOf(hasOperatorName("|="), hasOperatorName("+=")), hasRHS(enumExpr("enumExpr", "enumDecl"))), 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); } }
void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) { const auto WrongUse = anyOf( hasParent( binaryOperator( anyOf(has(integerLiteral(equals(0))), allOf(anyOf(hasOperatorName("<"), hasOperatorName(">="), hasOperatorName(">"), hasOperatorName("<=")), hasEitherOperand(integerLiteral(equals(1)))))) .bind("SizeBinaryOp")), hasParent(implicitCastExpr( hasImplicitDestinationType(isBoolType()), anyOf( hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")), anything()))), hasParent(explicitCastExpr(hasDestinationType(isBoolType())))); Finder->addMatcher( memberCallExpr( on(expr(anyOf(hasType(namedDecl(stlContainer())), hasType(pointsTo(namedDecl(stlContainer()))), hasType(references(namedDecl(stlContainer()))))) .bind("STLObject")), callee(methodDecl(hasName("size"))), WrongUse).bind("SizeCallExpr"), 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); }
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 ExplicitImplicitChecker::registerMatchers(MatchFinder* AstMatcher) { AstMatcher->addMatcher(cxxConstructorDecl(isInterestingImplicitCtor(), ofClass(allOf(isConcreteClass(), decl().bind("class"))), unless(isMarkedImplicit())) .bind("ctor"), this); }
TEST_F(StructuralEquivalenceCXXMethodTest, OutOfClass2) { auto t = makeDecls<FunctionDecl>( "struct X { virtual void f(); }; void X::f() { }", "struct X { void f(); }; void X::f() { }", Lang_CXX, functionDecl(allOf(hasName("f"), isDefinition()))); EXPECT_FALSE(testStructuralMatch(t)); }
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 SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) { const auto SignedIntegerOperand = expr(ignoringImpCasts(hasType(isSignedInteger()))).bind("signed-operand"); // The standard [bitmask.types] allows some integral types to be implemented // as signed types. Exclude these types from diagnosing for bitwise or(|) and // bitwise and(&). Shifting and complementing such values is still not // allowed. const auto BitmaskType = namedDecl(anyOf( hasName("::std::locale::category"), hasName("::std::ctype_base::mask"), hasName("::std::ios_base::fmtflags"), hasName("::std::ios_base::iostate"), hasName("::std::ios_base::openmode"))); const auto IsStdBitmask = ignoringImpCasts(declRefExpr(hasType(BitmaskType))); // Match binary bitwise operations on signed integer arguments. Finder->addMatcher( binaryOperator( allOf(anyOf(hasOperatorName("^"), hasOperatorName("|"), hasOperatorName("&"), hasOperatorName("^="), hasOperatorName("|="), hasOperatorName("&=")), unless(allOf(hasLHS(IsStdBitmask), hasRHS(IsStdBitmask))), hasEitherOperand(SignedIntegerOperand), hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger())))) .bind("binary-no-sign-interference"), this); // Shifting and complement is not allowed for any signed integer type because // the sign bit may corrupt the result. Finder->addMatcher( binaryOperator( allOf(anyOf(hasOperatorName("<<"), hasOperatorName(">>"), hasOperatorName("<<="), hasOperatorName(">>=")), hasEitherOperand(SignedIntegerOperand), hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger())))) .bind("binary-sign-interference"), this); // Match unary operations on signed integer types. Finder->addMatcher(unaryOperator(allOf(hasOperatorName("~"), hasUnaryOperand(SignedIntegerOperand))) .bind("unary-signed"), this); }
void NonMemMovableTemplateArgChecker::registerMatchers(MatchFinder* AstMatcher) { // Handle non-mem-movable template specializations AstMatcher->addMatcher( classTemplateSpecializationDecl( allOf(needsMemMovableTemplateArg(), hasAnyTemplateArgument(refersToType(isNonMemMovable())))) .bind("specialization"), this); }
void UnnecessaryValueParamCheck::registerMatchers(MatchFinder *Finder) { const auto ExpensiveValueParamDecl = parmVarDecl(hasType(hasCanonicalType(allOf(matchers::isExpensiveToCopy(), unless(referenceType())))), decl().bind("param")); Finder->addMatcher( functionDecl(isDefinition(), unless(cxxMethodDecl(isOverride())), unless(isInstantiated()), has(typeLoc(forEach(ExpensiveValueParamDecl))), decl().bind("functionDecl")), this); }
void MisplacedOperatorInStrlenInAllocCheck::registerMatchers( MatchFinder *Finder) { const auto StrLenFunc = functionDecl(anyOf( hasName("::strlen"), hasName("::std::strlen"), hasName("::strnlen"), hasName("::std::strnlen"), hasName("::strnlen_s"), hasName("::std::strnlen_s"), hasName("::wcslen"), hasName("::std::wcslen"), hasName("::wcsnlen"), hasName("::std::wcsnlen"), hasName("::wcsnlen_s"), hasName("std::wcsnlen_s"))); const auto BadUse = callExpr(callee(StrLenFunc), hasAnyArgument(ignoringImpCasts( binaryOperator( hasOperatorName("+"), hasRHS(ignoringParenImpCasts(integerLiteral(equals(1))))) .bind("BinOp")))) .bind("StrLen"); const auto BadArg = anyOf( allOf(unless(binaryOperator( hasOperatorName("+"), hasLHS(BadUse), hasRHS(ignoringParenImpCasts(integerLiteral(equals(1)))))), hasDescendant(BadUse)), BadUse); const auto Alloc0Func = functionDecl(anyOf(hasName("::malloc"), hasName("std::malloc"), hasName("::alloca"), hasName("std::alloca"))); const auto Alloc1Func = functionDecl(anyOf(hasName("::calloc"), hasName("std::calloc"), hasName("::realloc"), hasName("std::realloc"))); const auto Alloc0FuncPtr = varDecl(hasType(isConstQualified()), hasInitializer(ignoringParenImpCasts( declRefExpr(hasDeclaration(Alloc0Func))))); const auto Alloc1FuncPtr = varDecl(hasType(isConstQualified()), hasInitializer(ignoringParenImpCasts( declRefExpr(hasDeclaration(Alloc1Func))))); Finder->addMatcher(callExpr(callee(decl(anyOf(Alloc0Func, Alloc0FuncPtr))), hasArgument(0, BadArg)) .bind("Alloc"), this); Finder->addMatcher(callExpr(callee(decl(anyOf(Alloc1Func, Alloc1FuncPtr))), hasArgument(1, BadArg)) .bind("Alloc"), this); Finder->addMatcher( cxxNewExpr(isArray(), hasArraySize(BadArg)).bind("Alloc"), this); }
void StaticallyConstructedObjectsCheck::registerMatchers(MatchFinder *Finder) { // Constructing global, non-trivial objects with static storage is // disallowed, unless the object is statically initialized with a constexpr // constructor or has no explicit constructor. // Constexpr requires C++11 or later. if (!getLangOpts().CPlusPlus11) return; Finder->addMatcher( varDecl(allOf( // Match global, statically stored objects... isGlobalStatic(), // ... that have C++ constructors... hasDescendant(cxxConstructExpr(unless(allOf( // ... unless it is constexpr ... hasDeclaration(cxxConstructorDecl(isConstexpr())), // ... and is statically initialized. isConstantInitializer())))))) .bind("decl"), this); }
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 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 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) { Finder->addMatcher( constructorDecl(unless(isImplicit()), allOf( isMoveConstructor(), hasAnyConstructorInitializer( ctorInitializer(withInitializer(constructExpr(hasDeclaration( constructorDecl(isCopyConstructor()).bind("ctor") )))).bind("init") ) )), this); } }
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 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 InterfacesGlobalInitCheck::registerMatchers(MatchFinder *Finder) { const auto IsGlobal = allOf(hasGlobalStorage(), hasDeclContext(anyOf(translationUnitDecl(), // Global scope. namespaceDecl(), // Namespace scope. recordDecl())), // Class scope. unless(isConstexpr())); const auto ReferencesUndefinedGlobalVar = declRefExpr(hasDeclaration( varDecl(IsGlobal, unless(isDefinition())).bind("referencee"))); Finder->addMatcher( varDecl(IsGlobal, isDefinition(), hasInitializer(expr(hasDescendant(ReferencesUndefinedGlobalVar)))) .bind("var"), this); }
void FunctionNamingCheck::registerMatchers(MatchFinder *Finder) { // This check should only be applied to Objective-C sources. if (!getLangOpts().ObjC) return; // Enforce Objective-C function naming conventions on all functions except: // • Functions defined in system headers. // • C++ member functions. // • Namespaced functions. // • Implicitly defined functions. // • The main function. Finder->addMatcher( functionDecl( unless(anyOf(isExpansionInSystemHeader(), cxxMethodDecl(), hasAncestor(namespaceDecl()), isMain(), isImplicit(), matchesName(validFunctionNameRegex(true)), allOf(isStaticStorageClass(), matchesName(validFunctionNameRegex(false)))))) .bind("function"), this); }
namespace modernize { static const char AutoPtrTokenId[] = "AutoPrTokenId"; static const char AutoPtrOwnershipTransferId[] = "AutoPtrOwnershipTransferId"; /// \brief Matches expressions that are lvalues. /// /// In the following example, a[0] matches expr(isLValue()): /// \code /// std::string a[2]; /// std::string b; /// b = a[0]; /// b = "this string won't match"; /// \endcode AST_MATCHER(Expr, isLValue) { return Node.getValueKind() == VK_LValue; } /// Matches declarations whose declaration context is the C++ standard library /// namespace std. /// /// Note that inline namespaces are silently ignored during the lookup since /// both libstdc++ and libc++ are known to use them for versioning purposes. /// /// Given: /// \code /// namespace ns { /// struct my_type {}; /// using namespace std; /// } /// /// using std::vector; /// using ns:my_type; /// using ns::list; /// \code /// /// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace()))) /// matches "using std::vector" and "using ns::list". AST_MATCHER(Decl, isFromStdNamespace) { const DeclContext *D = Node.getDeclContext(); while (D->isInlineNamespace()) D = D->getParent(); if (!D->isNamespace() || !D->getParent()->isTranslationUnit()) return false; const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier(); return (Info && Info->isStr("std")); } /// \brief Matcher that finds auto_ptr declarations. static DeclarationMatcher AutoPtrDecl = recordDecl(hasName("auto_ptr"), isFromStdNamespace()); /// \brief Matches types declared as auto_ptr. static TypeMatcher AutoPtrType = qualType(hasDeclaration(AutoPtrDecl)); /// \brief Matcher that finds expressions that are candidates to be wrapped with /// 'std::move'. /// /// Binds the id \c AutoPtrOwnershipTransferId to the expression. static StatementMatcher MovableArgumentMatcher = expr(allOf(isLValue(), hasType(AutoPtrType))) .bind(AutoPtrOwnershipTransferId); /// \brief Creates a matcher that finds the locations of types referring to the /// \c std::auto_ptr() type. /// /// \code /// std::auto_ptr<int> a; /// ^~~~~~~~~~~~~ /// /// typedef std::auto_ptr<int> int_ptr_t; /// ^~~~~~~~~~~~~ /// /// std::auto_ptr<int> fn(std::auto_ptr<int>); /// ^~~~~~~~~~~~~ ^~~~~~~~~~~~~ /// /// <etc...> /// \endcode TypeLocMatcher makeAutoPtrTypeLocMatcher() { // Skip elaboratedType() as the named type will match soon thereafter. return typeLoc(loc(qualType(AutoPtrType, unless(elaboratedType())))) .bind(AutoPtrTokenId); } /// \brief Creates a matcher that finds the using declarations referring to /// \c std::auto_ptr. /// /// \code /// using std::auto_ptr; /// ^~~~~~~~~~~~~~~~~~~ /// \endcode DeclarationMatcher makeAutoPtrUsingDeclMatcher() { return usingDecl(hasAnyUsingShadowDecl(hasTargetDecl( allOf(hasName("auto_ptr"), isFromStdNamespace())))) .bind(AutoPtrTokenId); } /// \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)))); } /// \brief Locates the \c auto_ptr token when it is referred by a \c TypeLoc. /// /// \code /// std::auto_ptr<int> i; /// ^~~~~~~~~~~~~ /// \endcode /// /// The caret represents the location returned and the tildes cover the /// parameter \p AutoPtrTypeLoc. /// /// \return An invalid \c SourceLocation if not found, otherwise the location /// of the beginning of the \c auto_ptr token. static SourceLocation locateFromTypeLoc(const TypeLoc *AutoPtrTypeLoc, const SourceManager &SM) { auto TL = AutoPtrTypeLoc->getAs<TemplateSpecializationTypeLoc>(); if (TL.isNull()) return SourceLocation(); return TL.getTemplateNameLoc(); } /// \brief Locates the \c auto_ptr token in using declarations. /// /// \code /// using std::auto_ptr; /// ^ /// \endcode /// /// The caret represents the location returned. /// /// \return An invalid \c SourceLocation if not found, otherwise the location /// of the beginning of the \c auto_ptr token. static SourceLocation locateFromUsingDecl(const UsingDecl *UsingAutoPtrDecl, const SourceManager &SM) { return UsingAutoPtrDecl->getNameInfo().getBeginLoc(); } /// \brief Verifies that the token at \p TokenStart is 'auto_ptr'. static bool checkTokenIsAutoPtr(SourceLocation TokenStart, const SourceManager &SM, const LangOptions &LO) { SmallVector<char, 8> Buffer; bool Invalid = false; StringRef Res = Lexer::getSpelling(TokenStart, Buffer, SM, LO, &Invalid); return (!Invalid && Res == "auto_ptr"); } ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), IncludeStyle(utils::IncludeSorter::parseIncludeStyle( Options.get("IncludeStyle", "llvm"))) {} void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "IncludeStyle", utils::IncludeSorter::toString(IncludeStyle)); } 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) { Finder->addMatcher(makeAutoPtrTypeLocMatcher(), this); Finder->addMatcher(makeAutoPtrUsingDeclMatcher(), this); Finder->addMatcher(makeTransferOwnershipExprMatcher(), this); } } void ReplaceAutoPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) { // Only register the preprocessor callbacks for C++; the functionality // currently does not provide any benefit to other languages, despite being // benign. if (getLangOpts().CPlusPlus) { Inserter.reset(new utils::IncludeInserter( Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle)); Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks()); } } void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) { SourceManager &SM = *Result.SourceManager; if (const auto *E = Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) { CharSourceRange Range = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions()); if (Range.isInvalid()) return; auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership") << FixItHint::CreateInsertion(Range.getBegin(), "std::move(") << FixItHint::CreateInsertion(Range.getEnd(), ")"); auto Insertion = Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility", /*IsAngled=*/true); if (Insertion.hasValue()) Diag << Insertion.getValue(); return; } SourceLocation IdentifierLoc; if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) { IdentifierLoc = locateFromTypeLoc(TL, SM); } else if (const auto *D = Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) { IdentifierLoc = locateFromUsingDecl(D, SM); } else { llvm_unreachable("Bad Callback. No node provided."); } if (IdentifierLoc.isMacroID()) IdentifierLoc = SM.getSpellingLoc(IdentifierLoc); // Ensure that only the 'auto_ptr' token is replaced and not the template // aliases. if (!checkTokenIsAutoPtr(IdentifierLoc, SM, LangOptions())) return; SourceLocation EndLoc = IdentifierLoc.getLocWithOffset(strlen("auto_ptr") - 1); diag(IdentifierLoc, "auto_ptr is deprecated, use unique_ptr instead") << FixItHint::CreateReplacement(SourceRange(IdentifierLoc, EndLoc), "unique_ptr"); } } // namespace modernize
/// \brief Creates a matcher that finds the using declarations referring to /// \c std::auto_ptr. /// /// \code /// using std::auto_ptr; /// ^~~~~~~~~~~~~~~~~~~ /// \endcode DeclarationMatcher makeAutoPtrUsingDeclMatcher() { return usingDecl(hasAnyUsingShadowDecl(hasTargetDecl( allOf(hasName("auto_ptr"), isFromStdNamespace())))) .bind(AutoPtrTokenId); }
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); }
void DanglingOnTemporaryChecker::registerMatchers(MatchFinder *AstMatcher) { //////////////////////////////////////// // Quick annotation conflict checkers // //////////////////////////////////////// AstMatcher->addMatcher( // This is a matcher on a method declaration, cxxMethodDecl( // which is marked as no dangling on temporaries, noDanglingOnTemporaries(), // and which is && ref-qualified. isRValueRefQualified(), decl().bind("invalidMethodRefQualified")), this); AstMatcher->addMatcher( // This is a matcher on a method declaration, cxxMethodDecl( // which is marked as no dangling on temporaries, noDanglingOnTemporaries(), // which returns a primitive type, returns(builtinType()), // and which doesn't return a pointer. unless(returns(pointerType())), decl().bind("invalidMethodPointer")), this); ////////////////// // Main checker // ////////////////// auto hasParentCall = hasParent(expr(anyOf( cxxOperatorCallExpr( // If we're in a lamda, we may have an operator call expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // constructor call. unless(has(expr(ignoreTrivials(lambdaExpr())))), expr().bind("parentOperatorCallExpr")), callExpr( // If we're in a lamda, we may have a call expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // function call. unless(has(expr(ignoreTrivials(lambdaExpr())))), expr().bind("parentCallExpr")), objcMessageExpr( // If we're in a lamda, we may have an objc message expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // function call. unless(has(expr(ignoreTrivials(lambdaExpr())))), expr().bind("parentObjCMessageExpr")), cxxConstructExpr( // If we're in a lamda, we may have a construct expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // constructor call. unless(has(expr(ignoreTrivials(lambdaExpr())))), expr().bind("parentConstructExpr"))))); AstMatcher->addMatcher( // This is a matcher on a method call, cxxMemberCallExpr( // which is in first party code, isFirstParty(), // and which is performed on a temporary, on(allOf( unless(hasType(pointerType())), isTemporary(), // but which is not `this`. unless(cxxThisExpr()))), // and which is marked as no dangling on temporaries. callee(cxxMethodDecl(noDanglingOnTemporaries())), expr().bind("memberCallExpr"), // We optionally match a parent call expression or a parent construct // expression because using a temporary inside a call is fine as long // as the pointer doesn't escape the function call. anyOf( // This is the case where the call is the direct parent, so we // know that the member call expression is the argument. allOf(hasParentCall, expr().bind("parentCallArg")), // This is the case where the call is not the direct parent, so we // get its child to know in which argument tree we are. hasAncestor(expr( hasParentCall, expr().bind("parentCallArg"))), // To make it optional. anything())), this); }