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 AssignOperatorSignatureCheck::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) { const auto HasGoodReturnType = methodDecl(returns(lValueReferenceType( pointee(unless(isConstQualified()), hasDeclaration(equalsBoundNode("class")))))); const auto IsSelf = qualType(anyOf( hasDeclaration(equalsBoundNode("class")), referenceType(pointee(hasDeclaration(equalsBoundNode("class")))))); const auto IsSelfAssign = methodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())), hasName("operator="), ofClass(recordDecl().bind("class")), hasParameter(0, parmVarDecl(hasType(IsSelf)))) .bind("method"); Finder->addMatcher( methodDecl(IsSelfAssign, unless(HasGoodReturnType)).bind("ReturnType"), this); const auto BadSelf = referenceType( anyOf(lValueReferenceType(pointee(unless(isConstQualified()))), rValueReferenceType(pointee(isConstQualified())))); Finder->addMatcher( methodDecl(IsSelfAssign, hasParameter(0, parmVarDecl(hasType(BadSelf)))) .bind("ArgumentType"), this); Finder->addMatcher(methodDecl(IsSelfAssign, isConst()).bind("Const"), 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 FasterStringFindCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; const auto SingleChar = expr(ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal"))); const auto StringFindFunctions = anyOf(hasName("find"), hasName("rfind"), hasName("find_first_of"), hasName("find_first_not_of"), hasName("find_last_of"), hasName("find_last_not_of")); llvm::Optional<ast_matchers::internal::Matcher<NamedDecl>> IsStringClass; for (const auto &ClassName : StringLikeClasses) { const auto HasName = hasName(ClassName); IsStringClass = IsStringClass ? anyOf(*IsStringClass, HasName) : HasName; } if (IsStringClass) { Finder->addMatcher( cxxMemberCallExpr( callee(functionDecl(StringFindFunctions).bind("func")), anyOf(argumentCountIs(1), argumentCountIs(2)), hasArgument(0, SingleChar), on(expr(hasType(recordDecl(*IsStringClass)), unless(hasSubstitutedType())))), this); } }
void ProTypeUnionAccessCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; Finder->addMatcher( memberExpr(hasObjectExpression(hasType(recordDecl(isUnion())))) .bind("expr"), this); }
void CloexecAcceptCheck::registerMatchers(MatchFinder *Finder) { auto SockAddrPointerType = hasType(pointsTo(recordDecl(isStruct(), hasName("sockaddr")))); auto SockLenPointerType = hasType(pointsTo(namedDecl(hasName("socklen_t")))); registerMatchersImpl(Finder, functionDecl(returns(isInteger()), hasName("accept"), hasParameter(0, hasType(isInteger())), hasParameter(1, SockAddrPointerType), hasParameter(2, SockLenPointerType))); }
TEST_F(StructuralEquivalenceRecordTest, RecordsAreInequivalentIfOrderOfAnonRecordsIsDifferent) { auto t = makeTuDecls( R"( struct X { struct { int a; }; struct { int b; }; }; )", R"( struct X { // The order is reversed. struct { int b; }; struct { int a; }; }; )", Lang_C); auto *TU = get<0>(t); auto *A = FirstDeclMatcher<IndirectFieldDecl>().match( TU, indirectFieldDecl(hasName("a"))); auto *FA = cast<FieldDecl>(A->chain().front()); RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl(); auto *TU1 = get<1>(t); auto *A1 = FirstDeclMatcher<IndirectFieldDecl>().match( TU1, indirectFieldDecl(hasName("a"))); auto *FA1 = cast<FieldDecl>(A1->chain().front()); RecordDecl *RA1 = cast<RecordType>(FA1->getType().getTypePtr())->getDecl(); RecordDecl *X = FirstDeclMatcher<RecordDecl>().match(TU, recordDecl(hasName("X"))); RecordDecl *X1 = FirstDeclMatcher<RecordDecl>().match(TU1, recordDecl(hasName("X"))); ASSERT_NE(X, X1); EXPECT_FALSE(testStructuralMatch(X, X1)); ASSERT_NE(RA, RA1); EXPECT_TRUE(testStructuralMatch(RA, RA)); EXPECT_TRUE(testStructuralMatch(RA1, RA1)); EXPECT_FALSE(testStructuralMatch(RA1, RA)); }
TEST(FriendDecl, InstantiationSourceRange) { RangeVerifier<FriendDecl> Verifier; Verifier.expectRange(4, 3, 4, 35); EXPECT_TRUE(Verifier.match( "template <typename T> class S;\n" "template<class T> void operator+(S<T> x);\n" "template<class T> struct S {\n" " friend void operator+<>(S<T> src);\n" "};\n" "void test(S<double> s) { +s; }", friendDecl(hasParent(recordDecl(isTemplateInstantiation()))))); }
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 UnconventionalAssignOperatorCheck::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; const auto HasGoodReturnType = cxxMethodDecl(returns(lValueReferenceType( pointee(unless(isConstQualified()), anyOf(autoType(), hasDeclaration(equalsBoundNode("class"))))))); const auto IsSelf = qualType( anyOf(hasDeclaration(equalsBoundNode("class")), referenceType(pointee(hasDeclaration(equalsBoundNode("class")))))); const auto IsAssign = cxxMethodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())), hasName("operator="), ofClass(recordDecl().bind("class"))) .bind("method"); const auto IsSelfAssign = cxxMethodDecl(IsAssign, hasParameter(0, parmVarDecl(hasType(IsSelf)))) .bind("method"); Finder->addMatcher( cxxMethodDecl(IsAssign, unless(HasGoodReturnType)).bind("ReturnType"), this); const auto BadSelf = referenceType( anyOf(lValueReferenceType(pointee(unless(isConstQualified()))), rValueReferenceType(pointee(isConstQualified())))); Finder->addMatcher( cxxMethodDecl(IsSelfAssign, hasParameter(0, parmVarDecl(hasType(BadSelf)))) .bind("ArgumentType"), this); Finder->addMatcher( cxxMethodDecl(IsSelfAssign, anyOf(isConst(), isVirtual())).bind("cv"), this); const auto IsBadReturnStatement = returnStmt(unless(has(ignoringParenImpCasts( anyOf(unaryOperator(hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())), cxxOperatorCallExpr(argumentCountIs(1), callee(unresolvedLookupExpr()), hasArgument(0, cxxThisExpr()))))))); const auto IsGoodAssign = cxxMethodDecl(IsAssign, HasGoodReturnType); Finder->addMatcher(returnStmt(IsBadReturnStatement, forFunction(IsGoodAssign)) .bind("returnStmt"), 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 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 FasterStringFindCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; const auto SingleChar = expr(ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal"))); const auto StringFindFunctions = hasAnyName("find", "rfind", "find_first_of", "find_first_not_of", "find_last_of", "find_last_not_of"); Finder->addMatcher( cxxMemberCallExpr( callee(functionDecl(StringFindFunctions).bind("func")), anyOf(argumentCountIs(1), argumentCountIs(2)), hasArgument(0, SingleChar), on(expr( hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration( recordDecl(hasAnyName(SmallVector<StringRef, 4>( StringLikeClasses.begin(), StringLikeClasses.end()))))))), unless(hasSubstitutedType())))), this); }
TEST(MatchVerifier, NoMatch) { LocationVerifier<VarDecl> Verifier; Verifier.expectLocation(1, 1); EXPECT_FALSE(Verifier.match("int i;", recordDecl())); }
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
namespace ProgrammingLanguage { //match sturct/union/class DeclarationMatcher recordFieldDeclMatcherPL = recordDecl(has(fieldDecl())).bind("recordDecl"); }