From b641b914a36bbf1f35587b9f0bf652d691f97cee Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Fri, 3 May 2019 12:50:00 +0000 Subject: [PATCH] Added an AST matcher for declarations that are in the `std` namespace Reviewers: alexfh Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D61480 llvm-svn: 359876 --- .../bugprone/InaccurateEraseCheck.cpp | 4 -- clang/docs/LibASTMatchersReference.html | 23 +++++++++ clang/include/clang/ASTMatchers/ASTMatchers.h | 23 +++++++++ clang/lib/AST/DeclBase.cpp | 3 +- clang/lib/ASTMatchers/Dynamic/Registry.cpp | 1 + .../ASTMatchers/ASTMatchersNarrowingTest.cpp | 51 +++++++++++++++++++ 6 files changed, 100 insertions(+), 5 deletions(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/InaccurateEraseCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/InaccurateEraseCheck.cpp index b236e9925fc4..95babc7a567d 100644 --- a/clang-tools-extra/clang-tidy/bugprone/InaccurateEraseCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/InaccurateEraseCheck.cpp @@ -17,10 +17,6 @@ namespace clang { namespace tidy { namespace bugprone { -namespace { -AST_MATCHER(Decl, isInStdNamespace) { return Node.isInStdNamespace(); } -} - void InaccurateEraseCheck::registerMatchers(MatchFinder *Finder) { // Only register the matchers for C++; the functionality currently does not // provide any benefit to other languages, despite being benign. diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html index f0e965568da0..a053bd1bb5f6 100644 --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -2827,6 +2827,29 @@ by the compiler (eg. implicit default/copy constructors). +Matcher<Decl>isInStdNamespace +
Matches declarations in the namespace `std`, but not in nested namespaces.
+
+Given
+  class vector {};
+  namespace foo {
+    class vector {};
+    namespace std {
+      class vector {};
+    }
+  }
+  namespace std {
+    inline namespace __1 {
+      class vector {}; // #1
+      namespace experimental {
+        class vector {};
+      }
+    }
+  }
+cxxRecordDecl(hasName("vector"), isInStdNamespace()) will match only #1.
+
+ + Matcher<Decl>isPrivate
Matches private C++ declarations.
 
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 661dcbc76705..8bd4429f0ba3 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -6212,6 +6212,29 @@ AST_MATCHER(NamespaceDecl, isAnonymous) {
   return Node.isAnonymousNamespace();
 }
 
+/// Matches declarations in the namespace `std`, but not in nested namespaces.
+///
+/// Given
+/// \code
+///   class vector {};
+///   namespace foo {
+///     class vector {};
+///     namespace std {
+///       class vector {};
+///     }
+///   }
+///   namespace std {
+///     inline namespace __1 {
+///       class vector {}; // #1
+///       namespace experimental {
+///         class vector {};
+///       }
+///     }
+///   }
+/// \endcode
+/// cxxRecordDecl(hasName("vector"), isInStdNamespace()) will match only #1.
+AST_MATCHER(Decl, isInStdNamespace) { return Node.isInStdNamespace(); }
+
 /// If the given case statement does not use the GNU case range
 /// extension, matches the constant given in the statement.
 ///
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 5a6d85cd00c4..f40896da48d0 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -354,7 +354,8 @@ bool Decl::isInAnonymousNamespace() const {
 }
 
 bool Decl::isInStdNamespace() const {
-  return getDeclContext()->isStdNamespace();
+  const DeclContext *DC = getDeclContext();
+  return DC && DC->isStdNamespace();
 }
 
 TranslationUnitDecl *Decl::getTranslationUnitDecl() {
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index 37f1a0077a05..fedb9dd2abec 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -366,6 +366,7 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(isExternC);
   REGISTER_MATCHER(isFinal);
   REGISTER_MATCHER(isImplicit);
+  REGISTER_MATCHER(isInStdNamespace);
   REGISTER_MATCHER(isInTemplateInstantiation);
   REGISTER_MATCHER(isInline);
   REGISTER_MATCHER(isInstanceMessage);
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index 012ca117c299..01b168da2b77 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -2031,6 +2031,57 @@ TEST(NS, Anonymous) {
   EXPECT_TRUE(matches("namespace {}", namespaceDecl(isAnonymous())));
 }
 
+TEST(DeclarationMatcher, InStdNamespace) {
+  EXPECT_TRUE(notMatches("class vector {};"
+                         "namespace foo {"
+                         "  class vector {};"
+                         "}"
+                         "namespace foo {"
+                         "  namespace std {"
+                         "    class vector {};"
+                         "  }"
+                         "}",
+                         cxxRecordDecl(hasName("vector"), isInStdNamespace())));
+
+  EXPECT_TRUE(matches("namespace std {"
+                      "  class vector {};"
+                      "}",
+                      cxxRecordDecl(hasName("vector"), isInStdNamespace())));
+  EXPECT_TRUE(matches("namespace std {"
+                      "  inline namespace __1 {"
+                      "    class vector {};"
+                      "  }"
+                      "}",
+                      cxxRecordDecl(hasName("vector"), isInStdNamespace())));
+  EXPECT_TRUE(notMatches("namespace std {"
+                         "  inline namespace __1 {"
+                         "    inline namespace __fs {"
+                         "      namespace filesystem {"
+                         "        inline namespace v1 {"
+                         "          class path {};"
+                         "        }"
+                         "      }"
+                         "    }"
+                         "  }"
+                         "}",
+                         cxxRecordDecl(hasName("path"), isInStdNamespace())));
+  EXPECT_TRUE(
+      matches("namespace std {"
+              "  inline namespace __1 {"
+              "    inline namespace __fs {"
+              "      namespace filesystem {"
+              "        inline namespace v1 {"
+              "          class path {};"
+              "        }"
+              "      }"
+              "    }"
+              "  }"
+              "}",
+              cxxRecordDecl(hasName("path"),
+                            hasAncestor(namespaceDecl(hasName("filesystem"),
+                                                      isInStdNamespace())))));
+}
+
 TEST(EqualsBoundNodeMatcher, QualType) {
   EXPECT_TRUE(matches(
     "int i = 1;", varDecl(hasType(qualType().bind("type")),