From b3cf7e2fbf12bee1693cf158df14663b83776019 Mon Sep 17 00:00:00 2001 From: Manuel Klimek <klimek@google.com> Date: Tue, 25 Nov 2014 17:01:06 +0000 Subject: [PATCH] Re-apply r222646 (was reverted in r222667). Adding 4 ASTMatchers: typedefDecl, isInMainFile, isInSystemFile, isInFileMatchingName Change to original: ifndef out tests in Windows due to /-separated paths. Summary: Often one is only interested in matches within the main-file or matches that are not within a system-header, for which this patch adds isInMainFile and isInSystemFile. They take no arguments and narrow down the matches. The isInFileMatchingName is mainly thought for interactive clang-query-sessions, to make a matcher more specific without restarting the session with the files you are interested in for that moment. It takes a string that will be used as regular-expression to match the filename of where the matched node is expanded. Patch by Hendrik von Prince. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@222765 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LibASTMatchersReference.html | 147 +++++++++++++++++++++- include/clang/ASTMatchers/ASTMatchers.h | 92 ++++++++++++++ include/clang/Tooling/Tooling.h | 11 +- lib/ASTMatchers/Dynamic/Registry.cpp | 4 + lib/Tooling/Tooling.cpp | 14 ++- unittests/ASTMatchers/ASTMatchersTest.cpp | 53 ++++++++ unittests/ASTMatchers/ASTMatchersTest.h | 12 +- 7 files changed, 321 insertions(+), 12 deletions(-) diff --git a/docs/LibASTMatchersReference.html b/docs/LibASTMatchersReference.html index 2eabff49cff..e70d1ec3fac 100644 --- a/docs/LibASTMatchersReference.html +++ b/docs/LibASTMatchersReference.html @@ -307,6 +307,16 @@ Example matches X, Z </pre></td></tr> +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('typedefDecl0')"><a name="typedefDecl0Anchor">typedefDecl</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypedefDecl.html">TypedefDecl</a>>...</td></tr> +<tr><td colspan="4" class="doc" id="typedefDecl0"><pre>Matches typedef declarations. + +Given + typedef int X; +typedefDecl() + matches "typedef int X" +</pre></td></tr> + + <tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('unresolvedUsingValueDecl0')"><a name="unresolvedUsingValueDecl0Anchor">unresolvedUsingValueDecl</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1UnresolvedUsingValueDecl.html">UnresolvedUsingValueDecl</a>>...</td></tr> <tr><td colspan="4" class="doc" id="unresolvedUsingValueDecl0"><pre>Matches unresolved using value declarations. @@ -339,6 +349,15 @@ usingDirectiveDecl() matches using namespace X </pre></td></tr> +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('valueDecl0')"><a name="valueDecl0Anchor">valueDecl</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html">ValueDecl</a>>...</td></tr> +<tr><td colspan="4" class="doc" id="valueDecl0"><pre>Matches any value declaration. + +Example matches A, B, C and F + enum X { A, B, C }; + void F(); +</pre></td></tr> + + <tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('varDecl0')"><a name="varDecl0Anchor">varDecl</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>>...</td></tr> <tr><td colspan="4" class="doc" id="varDecl0"><pre>Matches variable declarations. @@ -1654,6 +1673,48 @@ f. </pre></td></tr> +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('isExpansionInFileMatching0')"><a name="isExpansionInFileMatching0Anchor">isExpansionInFileMatching</a></td><td>std::string RegExp</td></tr> +<tr><td colspan="4" class="doc" id="isExpansionInFileMatching0"><pre>Matches AST nodes that were expanded within files whose name is +partially matching a given regex. + +Example matches Y but not X + (matcher = recordDecl(isExpansionInFileMatching("AST.*")) + #include "ASTMatcher.h" + class X {}; +ASTMatcher.h: + class Y {}; + +Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>> +</pre></td></tr> + + +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('isExpansionInMainFile0')"><a name="isExpansionInMainFile0Anchor">isExpansionInMainFile</a></td><td></td></tr> +<tr><td colspan="4" class="doc" id="isExpansionInMainFile0"><pre>Matches AST nodes that were expanded within the main-file. + +Example matches X but not Y (matcher = recordDecl(isExpansionInMainFile()) + #include <Y.h> + class X {}; +Y.h: + class Y {}; + +Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>> +</pre></td></tr> + + +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('isExpansionInSystemHeader0')"><a name="isExpansionInSystemHeader0Anchor">isExpansionInSystemHeader</a></td><td></td></tr> +<tr><td colspan="4" class="doc" id="isExpansionInSystemHeader0"><pre>Matches AST nodes that were expanded within system-header-files. + +Example matches Y but not X + (matcher = recordDecl(isExpansionInSystemHeader()) + #include <SystemHeader.h> + class X {}; +SystemHeader.h: + class Y {}; + +Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>> +</pre></td></tr> + + <tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('isImplicit0')"><a name="isImplicit0Anchor">isImplicit</a></td><td></td></tr> <tr><td colspan="4" class="doc" id="isImplicit0"><pre>Matches a declaration that has been implicitly added by the compiler (eg. implicit defaultcopy constructors). @@ -1858,7 +1919,7 @@ memberExpr(isArrow()) </pre></td></tr> -<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1NamedDecl.html">NamedDecl</a>></td><td class="name" onclick="toggle('hasName0')"><a name="hasName0Anchor">hasName</a></td><td>std::string Name</td></tr> +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1NamedDecl.html">NamedDecl</a>></td><td class="name" onclick="toggle('hasName0')"><a name="hasName0Anchor">hasName</a></td><td>std::string Name</td></tr> <tr><td colspan="4" class="doc" id="hasName0"><pre>Matches NamedDecl nodes that have the specified name. Supports specifying enclosing namespaces or classes by prefixing the name @@ -1990,6 +2051,48 @@ and reference to that variable declaration within a compound statement. </pre></td></tr> +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('isExpansionInFileMatching1')"><a name="isExpansionInFileMatching1Anchor">isExpansionInFileMatching</a></td><td>std::string RegExp</td></tr> +<tr><td colspan="4" class="doc" id="isExpansionInFileMatching1"><pre>Matches AST nodes that were expanded within files whose name is +partially matching a given regex. + +Example matches Y but not X + (matcher = recordDecl(isExpansionInFileMatching("AST.*")) + #include "ASTMatcher.h" + class X {}; +ASTMatcher.h: + class Y {}; + +Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>> +</pre></td></tr> + + +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('isExpansionInMainFile1')"><a name="isExpansionInMainFile1Anchor">isExpansionInMainFile</a></td><td></td></tr> +<tr><td colspan="4" class="doc" id="isExpansionInMainFile1"><pre>Matches AST nodes that were expanded within the main-file. + +Example matches X but not Y (matcher = recordDecl(isExpansionInMainFile()) + #include <Y.h> + class X {}; +Y.h: + class Y {}; + +Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>> +</pre></td></tr> + + +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('isExpansionInSystemHeader1')"><a name="isExpansionInSystemHeader1Anchor">isExpansionInSystemHeader</a></td><td></td></tr> +<tr><td colspan="4" class="doc" id="isExpansionInSystemHeader1"><pre>Matches AST nodes that were expanded within system-header-files. + +Example matches Y but not X + (matcher = recordDecl(isExpansionInSystemHeader()) + #include <SystemHeader.h> + class X {}; +SystemHeader.h: + class Y {}; + +Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>> +</pre></td></tr> + + <tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('isInTemplateInstantiation0')"><a name="isInTemplateInstantiation0Anchor">isInTemplateInstantiation</a></td><td></td></tr> <tr><td colspan="4" class="doc" id="isInTemplateInstantiation0"><pre>Matches statements inside of a template instantiation. @@ -2061,6 +2164,48 @@ classTemplateSpecializationDecl(templateArgumentCountIs(1)) </pre></td></tr> +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>></td><td class="name" onclick="toggle('isExpansionInFileMatching2')"><a name="isExpansionInFileMatching2Anchor">isExpansionInFileMatching</a></td><td>std::string RegExp</td></tr> +<tr><td colspan="4" class="doc" id="isExpansionInFileMatching2"><pre>Matches AST nodes that were expanded within files whose name is +partially matching a given regex. + +Example matches Y but not X + (matcher = recordDecl(isExpansionInFileMatching("AST.*")) + #include "ASTMatcher.h" + class X {}; +ASTMatcher.h: + class Y {}; + +Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>> +</pre></td></tr> + + +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>></td><td class="name" onclick="toggle('isExpansionInMainFile2')"><a name="isExpansionInMainFile2Anchor">isExpansionInMainFile</a></td><td></td></tr> +<tr><td colspan="4" class="doc" id="isExpansionInMainFile2"><pre>Matches AST nodes that were expanded within the main-file. + +Example matches X but not Y (matcher = recordDecl(isExpansionInMainFile()) + #include <Y.h> + class X {}; +Y.h: + class Y {}; + +Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>> +</pre></td></tr> + + +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>></td><td class="name" onclick="toggle('isExpansionInSystemHeader2')"><a name="isExpansionInSystemHeader2Anchor">isExpansionInSystemHeader</a></td><td></td></tr> +<tr><td colspan="4" class="doc" id="isExpansionInSystemHeader2"><pre>Matches AST nodes that were expanded within system-header-files. + +Example matches Y but not X + (matcher = recordDecl(isExpansionInSystemHeader()) + #include <SystemHeader.h> + class X {}; +SystemHeader.h: + class Y {}; + +Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>> +</pre></td></tr> + + <tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('equalsBoundNode2')"><a name="equalsBoundNode2Anchor">equalsBoundNode</a></td><td>std::string ID</td></tr> <tr><td colspan="4" class="doc" id="equalsBoundNode2"><pre>Matches if a node equals a previously bound node. diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h index 37c7b16934b..cbdca79fc46 100644 --- a/include/clang/ASTMatchers/ASTMatchers.h +++ b/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,97 @@ typedef internal::Matcher<NestedNameSpecifierLoc> NestedNameSpecifierLocMatcher; /// 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<Decl, TypedefDecl> typedefDecl; + +/// \brief Matches AST nodes that were expanded within the main-file. +/// +/// Example matches X but not Y (matcher = recordDecl(isExpansionInMainFile()) +/// \code +/// #include <Y.h> +/// class X {}; +/// \endcode +/// Y.h: +/// \code +/// class Y {}; +/// \endcode +/// +/// Usable as: Matcher<Decl>, Matcher<Stmt>, Matcher<TypeLoc> +AST_POLYMORPHIC_MATCHER(isExpansionInMainFile, + 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(isExpansionInSystemHeader()) +/// \code +/// #include <SystemHeader.h> +/// class X {}; +/// \endcode +/// SystemHeader.h: +/// \code +/// class Y {}; +/// \endcode +/// +/// Usable as: Matcher<Decl>, Matcher<Stmt>, Matcher<TypeLoc> +AST_POLYMORPHIC_MATCHER(isExpansionInSystemHeader, + 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(isExpansionInFileMatching("AST.*")) +/// \code +/// #include "ASTMatcher.h" +/// class X {}; +/// \endcode +/// ASTMatcher.h: +/// \code +/// class Y {}; +/// \endcode +/// +/// Usable as: Matcher<Decl>, Matcher<Stmt>, Matcher<TypeLoc> +AST_POLYMORPHIC_MATCHER_P(isExpansionInFileMatching, + 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; diff --git a/include/clang/Tooling/Tooling.h b/include/clang/Tooling/Tooling.h index 89a4798b8cd..55152ffd29e 100644 --- a/include/clang/Tooling/Tooling.h +++ b/include/clang/Tooling/Tooling.h @@ -143,6 +143,10 @@ inline std::unique_ptr<FrontendActionFactory> newFrontendActionFactory( bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code, const Twine &FileName = "input.cc"); +/// The first part of the pair is the filename, the second part the +/// file-content. +typedef std::vector<std::pair<std::string, std::string>> FileContentMappings; + /// \brief Runs (and deletes) the tool on 'Code' with the -fsyntax-only flag and /// with additional other flags. /// @@ -152,9 +156,10 @@ bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code, /// \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<std::string> &Args, - const Twine &FileName = "input.cc"); +bool runToolOnCodeWithArgs( + clang::FrontendAction *ToolAction, const Twine &Code, + const std::vector<std::string> &Args, const Twine &FileName = "input.cc", + const FileContentMappings &VirtualMappedFiles = FileContentMappings()); /// \brief Builds an AST for 'Code'. /// diff --git a/lib/ASTMatchers/Dynamic/Registry.cpp b/lib/ASTMatchers/Dynamic/Registry.cpp index dab41871714..c0816ace44e 100644 --- a/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/lib/ASTMatchers/Dynamic/Registry.cpp @@ -242,7 +242,10 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(isExpr); REGISTER_MATCHER(isExternC); REGISTER_MATCHER(isImplicit); + REGISTER_MATCHER(isExpansionInFileMatching); + REGISTER_MATCHER(isExpansionInMainFile); REGISTER_MATCHER(isInstantiated); + REGISTER_MATCHER(isExpansionInSystemHeader); REGISTER_MATCHER(isInteger); REGISTER_MATCHER(isIntegral); REGISTER_MATCHER(isInTemplateInstantiation); @@ -314,6 +317,7 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(to); REGISTER_MATCHER(tryStmt); REGISTER_MATCHER(type); + REGISTER_MATCHER(typedefDecl); REGISTER_MATCHER(typedefType); REGISTER_MATCHER(typeLoc); REGISTER_MATCHER(unaryExprOrTypeTraitExpr); diff --git a/lib/Tooling/Tooling.cpp b/lib/Tooling/Tooling.cpp index 64613ded3e2..95ef03eb561 100644 --- a/lib/Tooling/Tooling.cpp +++ b/lib/Tooling/Tooling.cpp @@ -123,17 +123,25 @@ getSyntaxOnlyToolArgs(const std::vector<std::string> &ExtraArgs, bool runToolOnCodeWithArgs(clang::FrontendAction *ToolAction, const Twine &Code, const std::vector<std::string> &Args, - const Twine &FileName) { + const Twine &FileName, + const FileContentMappings &VirtualMappedFiles) { + SmallString<16> FileNameStorage; StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage); llvm::IntrusiveRefCntPtr<FileManager> 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(); } diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index c88a197643a..1304d64a187 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -4625,5 +4625,58 @@ TEST(EqualsBoundNodeMatcher, UnlessDescendantsOfAncestorsMatch) { .bind("data"))); } +TEST(TypeDefDeclMatcher, Match) { + EXPECT_TRUE(matches("typedef int typedefDeclTest;", + typedefDecl(hasName("typedefDeclTest")))); +} + +// FIXME: Figure out how to specify paths so the following tests pass on Windows. +#ifndef LLVM_ON_WIN32 + +TEST(Matcher, IsExpansionInMainFileMatcher) { + EXPECT_TRUE(matches("class X {};", + recordDecl(hasName("X"), isExpansionInMainFile()))); + EXPECT_TRUE(notMatches("", recordDecl(isExpansionInMainFile()))); + FileContentMappings M; + M.push_back(std::make_pair("/other", "class X {};")); + EXPECT_TRUE(matchesConditionally("#include <other>\n", + recordDecl(isExpansionInMainFile()), false, + "-isystem/", M)); +} + +TEST(Matcher, IsExpansionInSystemHeader) { + FileContentMappings M; + M.push_back(std::make_pair("/other", "class X {};")); + EXPECT_TRUE(matchesConditionally( + "#include \"other\"\n", recordDecl(isExpansionInSystemHeader()), true, + "-isystem/", M)); + EXPECT_TRUE(matchesConditionally("#include \"other\"\n", + recordDecl(isExpansionInSystemHeader()), + false, "-I/", M)); + EXPECT_TRUE(notMatches("class X {};", + recordDecl(isExpansionInSystemHeader()))); + EXPECT_TRUE(notMatches("", recordDecl(isExpansionInSystemHeader()))); +} + +TEST(Matcher, IsExpansionInFileMatching) { + FileContentMappings M; + M.push_back(std::make_pair("/foo", "class A {};")); + M.push_back(std::make_pair("/bar", "class B {};")); + EXPECT_TRUE(matchesConditionally( + "#include <foo>\n" + "#include <bar>\n" + "class X {};", + recordDecl(isExpansionInFileMatching("b.*"), hasName("B")), true, + "-isystem/", M)); + EXPECT_TRUE(matchesConditionally( + "#include <foo>\n" + "#include <bar>\n" + "class X {};", + recordDecl(isExpansionInFileMatching("f.*"), hasName("X")), false, + "-isystem/", M)); +} + +#endif // LLVM_ON_WIN32 + } // end namespace ast_matchers } // end namespace clang diff --git a/unittests/ASTMatchers/ASTMatchersTest.h b/unittests/ASTMatchers/ASTMatchersTest.h index 2e5b3da5a5f..a2ab9feee2a 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.h +++ b/unittests/ASTMatchers/ASTMatchersTest.h @@ -22,6 +22,7 @@ using clang::tooling::buildASTFromCodeWithArgs; using clang::tooling::newFrontendActionFactory; using clang::tooling::runToolOnCodeWithArgs; using clang::tooling::FrontendActionFactory; +using clang::tooling::FileContentMappings; class BoundNodesCallback { public: @@ -58,10 +59,10 @@ private: }; template <typename T> -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 FileContentMappings &VirtualMappedFiles = FileContentMappings()) { bool Found = false, DynamicFound = false; MatchFinder Finder; VerifyMatch VerifyFound(nullptr, &Found); @@ -73,7 +74,8 @@ testing::AssertionResult matchesConditionally(const std::string &Code, newFrontendActionFactory(&Finder)); // Some tests use typeof, which is a gnu extension. std::vector<std::string> 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) { -- GitLab