Index: include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- include/clang/ASTMatchers/ASTMatchers.h +++ include/clang/ASTMatchers/ASTMatchers.h @@ -145,6 +145,95 @@ return internal::PolymorphicMatcherWithParam0(); } + +/// \brief Matches typedef declarations. +/// +/// Given +/// \code +/// typedef int X; +/// \endcode +/// typedefType(isInMainFile()) +/// 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 +/// matching a given regex +/// +/// 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_P(isInFileMatchingName, + AST_POLYMORPHIC_SUPPORTED_TYPES_3(Decl, Stmt, TypeLoc), + std::string, RegExp) { + assert(!RegExp.empty()); + + 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/ASTMatchers/ASTMatchersInternal.h =================================================================== --- include/clang/ASTMatchers/ASTMatchersInternal.h +++ include/clang/ASTMatchers/ASTMatchersInternal.h @@ -35,6 +35,7 @@ #ifndef LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_INTERNAL_H #define LLVM_CLANG_AST_MATCHERS_AST_MATCHERS_INTERNAL_H +#include "clang/AST/ASTContext.h" #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" Index: include/clang/Tooling/Tooling.h =================================================================== --- include/clang/Tooling/Tooling.h +++ include/clang/Tooling/Tooling.h @@ -153,7 +153,8 @@ /// \return - True if 'ToolAction' was successfully executed. bool runToolOnCodeWithArgs(clang::FrontendAction *ToolAction, const Twine &Code, const std::vector &Args, - const Twine &FileName = "input.cc"); + const Twine &FileName = "input.cc", + const std::vector> &VirtualMappedFiles= {}); /// \brief Builds an AST for 'Code'. /// Index: lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- lib/ASTMatchers/Dynamic/Registry.cpp +++ lib/ASTMatchers/Dynamic/Registry.cpp @@ -312,6 +312,10 @@ REGISTER_MATCHER(variableArrayType); REGISTER_MATCHER(whileStmt); REGISTER_MATCHER(withInitializer); + REGISTER_MATCHER(typedefDecl); + REGISTER_MATCHER(isInMainFile); + REGISTER_MATCHER(isInSystemHeader); + REGISTER_MATCHER(isInFileMatchingName); } RegistryMaps::~RegistryMaps() { Index: lib/Tooling/Tooling.cpp =================================================================== --- lib/Tooling/Tooling.cpp +++ lib/Tooling/Tooling.cpp @@ -121,19 +121,28 @@ 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.getPtr()); + ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), + ToolAction, Files.getPtr()); + 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 @@ -61,7 +61,8 @@ testing::AssertionResult matchesConditionally(const std::string &Code, const T &AMatcher, bool ExpectMatch, - llvm::StringRef CompileArg) { + llvm::StringRef CompileArg, + const std::vector> &VirtualMappedFiles= {}) { bool Found = false, DynamicFound = false; MatchFinder Finder; VerifyMatch VerifyFound(nullptr, &Found); @@ -73,7 +74,7 @@ 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 @@ -4397,5 +4397,57 @@ .bind("data"))); } +TEST(TypeDefDeclMatcher, Match) { + EXPECT_TRUE(matches("typedef int typedefDeclTest;", + typedefDecl(hasName("typedefDeclTest")))); +} + +TEST(Matcher, IsInMainFileMatcher) { + EXPECT_TRUE(matchesConditionally( + "class X {};", recordDecl(hasName("X"), isInMainFile()), true, "")); + EXPECT_TRUE(matchesConditionally("", recordDecl(isInMainFile()), false, "")); + 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(matchesConditionally("class X {};", + recordDecl(isInSystemHeader()), false, "")); + EXPECT_TRUE( + matchesConditionally("", recordDecl(isInSystemHeader()), false, "")); +} + +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 {};"}})); + ASSERT_DEBUG_DEATH({ + EXPECT_TRUE(matchesConditionally( + "#include \n" + "#include \n" + "class X {};", + recordDecl(isInFileMatchingName(""), hasName("X")), + true, "-isystem/", + {{"/foo", "class A {};"}, {"/bar", "class B {};"}})); + }, + ""); +} + } // end namespace ast_matchers } // end namespace clang