Index: include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- include/clang/ASTMatchers/ASTMatchers.h +++ include/clang/ASTMatchers/ASTMatchers.h @@ -47,6 +47,7 @@ #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchersInternal.h" #include "clang/ASTMatchers/ASTMatchersMacros.h" #include "llvm/ADT/Twine.h" @@ -142,6 +143,95 @@ /// Usable as: Any Matcher inline internal::TrueMatcher anything() { return internal::TrueMatcher(); } +/// \brief Matches typedef declarations. +/// +/// Given +/// \code +/// typedef int X; +/// \endcode +/// typedefDecl() +/// matches "typedef int X" +const internal::VariadicDynCastAllOfMatcher typedefDecl; + +/// \brief Matches AST nodes that were expanded within the main-file +/// +/// Example matches X but not Y (matcher = recordDecl(isInMainFile()) +/// \code +/// #include +/// class X {}; +/// \endcode +/// Y.h: +/// \code +/// class Y {}; +/// \endcode +/// +/// Usable as: Matcher, Matcher, Matcher +AST_POLYMORPHIC_MATCHER(isInMainFile, + AST_POLYMORPHIC_SUPPORTED_TYPES_3(Decl, Stmt, + TypeLoc)) { + auto &SourceManager = Finder->getASTContext().getSourceManager(); + return SourceManager.isInMainFile( + SourceManager.getExpansionLoc(Node.getLocStart())); +} + +/// \brief Matches AST nodes that were expanded within system-header-files +/// +/// Example matches Y but not X (matcher = recordDecl(isInSytemHeader()) +/// \code +/// #include +/// class X {}; +/// \endcode +/// SystemHeader.h: +/// \code +/// class Y {}; +/// \endcode +/// +/// Usable as: Matcher, Matcher, Matcher +AST_POLYMORPHIC_MATCHER(isInSystemHeader, + AST_POLYMORPHIC_SUPPORTED_TYPES_3(Decl, Stmt, + TypeLoc)) { + auto &SourceManager = Finder->getASTContext().getSourceManager(); + auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getLocStart()); + if (ExpansionLoc.isInvalid()) { + return false; + } + return SourceManager.isInSystemHeader(ExpansionLoc); +} + +/// \brief Matches AST nodes that were expanded within files whose name is +/// partially matching a given regex +/// +/// Example matches Y but not X (matcher = recordDecl(isInFileMatchingName("AST.*")) +/// \code +/// #include "ASTMatcher.h" +/// class X {}; +/// \endcode +/// ASTMatcher.h: +/// \code +/// class Y {}; +/// \endcode +/// +/// Usable as: Matcher, Matcher, Matcher +AST_POLYMORPHIC_MATCHER_P(isInFileMatchingName, + AST_POLYMORPHIC_SUPPORTED_TYPES_3(Decl, Stmt, + TypeLoc), + std::string, RegExp) { + auto &SourceManager = Finder->getASTContext().getSourceManager(); + auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getLocStart()); + if (ExpansionLoc.isInvalid()) { + return false; + } + auto FileEntry = + SourceManager.getFileEntryForID(SourceManager.getFileID(ExpansionLoc)); + if (!FileEntry) { + return false; + } + + auto Filename = FileEntry->getName(); + llvm::Regex RE(RegExp); + return RE.match(Filename); +} + /// \brief Matches declarations. /// /// Examples matches \c X, \c C, and the friend declaration inside \c C; Index: include/clang/Tooling/Tooling.h =================================================================== --- include/clang/Tooling/Tooling.h +++ include/clang/Tooling/Tooling.h @@ -152,9 +152,11 @@ /// \param FileName The file name which 'Code' will be mapped as. /// /// \return - True if 'ToolAction' was successfully executed. -bool runToolOnCodeWithArgs(clang::FrontendAction *ToolAction, const Twine &Code, - const std::vector &Args, - const Twine &FileName = "input.cc"); +bool runToolOnCodeWithArgs( + clang::FrontendAction *ToolAction, const Twine &Code, + const std::vector &Args, const Twine &FileName = "input.cc", + const std::vector> &VirtualMappedFiles = + std::vector>()); /// \brief Builds an AST for 'Code'. /// Index: lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- lib/ASTMatchers/Dynamic/Registry.cpp +++ lib/ASTMatchers/Dynamic/Registry.cpp @@ -242,7 +242,10 @@ REGISTER_MATCHER(isExpr); REGISTER_MATCHER(isExternC); REGISTER_MATCHER(isImplicit); + REGISTER_MATCHER(isInFileMatchingName); + REGISTER_MATCHER(isInMainFile); REGISTER_MATCHER(isInstantiated); + REGISTER_MATCHER(isInSystemHeader); REGISTER_MATCHER(isInteger); REGISTER_MATCHER(isIntegral); REGISTER_MATCHER(isInTemplateInstantiation); @@ -314,6 +317,7 @@ REGISTER_MATCHER(to); REGISTER_MATCHER(tryStmt); REGISTER_MATCHER(type); + REGISTER_MATCHER(typedefDecl); REGISTER_MATCHER(typedefType); REGISTER_MATCHER(typeLoc); REGISTER_MATCHER(unaryExprOrTypeTraitExpr); Index: lib/Tooling/Tooling.cpp =================================================================== --- lib/Tooling/Tooling.cpp +++ lib/Tooling/Tooling.cpp @@ -121,19 +121,29 @@ return Args; } -bool runToolOnCodeWithArgs(clang::FrontendAction *ToolAction, const Twine &Code, - const std::vector &Args, - const Twine &FileName) { +bool +runToolOnCodeWithArgs(clang::FrontendAction *ToolAction, const Twine &Code, + const std::vector &Args, + const Twine &FileName, + const std::vector> & + VirtualMappedFiles) { + SmallString<16> FileNameStorage; StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage); llvm::IntrusiveRefCntPtr Files( new FileManager(FileSystemOptions())); - ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), ToolAction, - Files.get()); + ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), + ToolAction, Files.get()); SmallString<1024> CodeStorage; Invocation.mapVirtualFile(FileNameRef, Code.toNullTerminatedStringRef(CodeStorage)); + + for (auto &FilenameWithContent : VirtualMappedFiles) { + Invocation.mapVirtualFile(FilenameWithContent.first, + FilenameWithContent.second); + } + return Invocation.run(); } Index: unittests/ASTMatchers/ASTMatchersTest.h =================================================================== --- unittests/ASTMatchers/ASTMatchersTest.h +++ unittests/ASTMatchers/ASTMatchersTest.h @@ -58,10 +58,11 @@ }; template -testing::AssertionResult matchesConditionally(const std::string &Code, - const T &AMatcher, - bool ExpectMatch, - llvm::StringRef CompileArg) { +testing::AssertionResult matchesConditionally( + const std::string &Code, const T &AMatcher, bool ExpectMatch, + llvm::StringRef CompileArg, + const std::vector> &VirtualMappedFiles = + std::vector>()) { bool Found = false, DynamicFound = false; MatchFinder Finder; VerifyMatch VerifyFound(nullptr, &Found); @@ -73,7 +74,8 @@ newFrontendActionFactory(&Finder)); // Some tests use typeof, which is a gnu extension. std::vector Args(1, CompileArg); - if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) { + if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, "input.cc", + VirtualMappedFiles)) { return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; } if (Found != DynamicFound) { Index: unittests/ASTMatchers/ASTMatchersTest.cpp =================================================================== --- unittests/ASTMatchers/ASTMatchersTest.cpp +++ unittests/ASTMatchers/ASTMatchersTest.cpp @@ -4597,5 +4597,45 @@ .bind("data"))); } +TEST(TypeDefDeclMatcher, Match) { + EXPECT_TRUE(matches("typedef int typedefDeclTest;", + typedefDecl(hasName("typedefDeclTest")))); +} + +TEST(Matcher, IsInMainFileMatcher) { + EXPECT_TRUE(matches( + "class X {};", recordDecl(hasName("X"), isInMainFile()))); + EXPECT_TRUE(notMatches("", recordDecl(isInMainFile()))); + EXPECT_TRUE(matchesConditionally("#include \n", + recordDecl(isInMainFile()), false, + "-isystem/", {{"/other", "class X {};"}})); +} + +TEST(Matcher, IsInSystemHeaderMatcher) { + EXPECT_TRUE(matchesConditionally("#include \"other\"\n", + recordDecl(isInSystemHeader()), true, + "-isystem/", {{"/other", "class X {};"}})); + EXPECT_TRUE(matchesConditionally("#include \"other\"\n", + recordDecl(isInSystemHeader()), false, "-I/", + {{"/other", "class X {};"}})); + EXPECT_TRUE(notMatches("class X {};", recordDecl(isInSystemHeader()))); + EXPECT_TRUE(notMatches("", recordDecl(isInSystemHeader()))); +} + +TEST(Matcher, IsInFileMatchingName) { + EXPECT_TRUE(matchesConditionally( + "#include \n" + "#include \n" + "class X {};", + recordDecl(isInFileMatchingName("b.*"), hasName("B")), true, "-isystem/", + {{"/foo", "class A {};"}, {"/bar", "class B {};"}})); + EXPECT_TRUE(matchesConditionally( + "#include \n" + "#include \n" + "class X {};", + recordDecl(isInFileMatchingName("f.*"), hasName("X")), false, "-isystem/", + {{"/foo", "class A {};"}, {"/bar", "class B {};"}})); +} + } // end namespace ast_matchers } // end namespace clang