r259898 - [ASTMatchers] Allow hasName() to look through inline namespaces
Samuel Benzaquen via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 5 10:29:24 PST 2016
Author: sbenza
Date: Fri Feb 5 12:29:24 2016
New Revision: 259898
URL: http://llvm.org/viewvc/llvm-project?rev=259898&view=rev
Log:
[ASTMatchers] Allow hasName() to look through inline namespaces
Summary:
Allow hasName() to look through inline namespaces.
This will fix the interaction between some clang-tidy checks and libc++.
libc++ defines names in an inline namespace named std::<version_#>.
When we try to match a name using hasName("std::xxx") it fails to match and the clang-tidy check does not work.
Reviewers: klimek
Subscribers: klimek, cfe-commits
Differential Revision: http://reviews.llvm.org/D15506
Modified:
cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h
cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp
cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp
Modified: cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h?rev=259898&r1=259897&r2=259898&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h Fri Feb 5 12:29:24 2016
@@ -653,10 +653,18 @@ class HasNameMatcher : public SingleNode
/// \brief Full match routine
///
+ /// Fast implementation for the simple case of a named declaration at
+ /// namespace or RecordDecl scope.
+ /// It is slower than matchesNodeUnqualified, but faster than
+ /// matchesNodeFullSlow.
+ bool matchesNodeFullFast(const NamedDecl &Node) const;
+
+ /// \brief Full match routine
+ ///
/// It generates the fully qualified name of the declaration (which is
/// expensive) before trying to match.
/// It is slower but simple and works on all cases.
- bool matchesNodeFull(const NamedDecl &Node) const;
+ bool matchesNodeFullSlow(const NamedDecl &Node) const;
const bool UseUnqualifiedMatch;
const std::string Name;
Modified: cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp?rev=259898&r1=259897&r2=259898&view=diff
==============================================================================
--- cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp (original)
+++ cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp Fri Feb 5 12:29:24 2016
@@ -298,45 +298,149 @@ HasNameMatcher::HasNameMatcher(StringRef
assert(!Name.empty());
}
-bool HasNameMatcher::matchesNodeUnqualified(const NamedDecl &Node) const {
- assert(UseUnqualifiedMatch);
- if (Node.getIdentifier()) {
- // Simple name.
- return Name == Node.getName();
+namespace {
+
+bool ConsumeNameSuffix(StringRef &FullName, StringRef Suffix) {
+ StringRef Name = FullName;
+ if (!Name.endswith(Suffix))
+ return false;
+ Name = Name.drop_back(Suffix.size());
+ if (!Name.empty()) {
+ if (!Name.endswith("::"))
+ return false;
+ Name = Name.drop_back(2);
}
+ FullName = Name;
+ return true;
+}
+
+bool ConsumeNodeName(StringRef &Name, const NamedDecl &Node) {
+ // Simple name.
+ if (Node.getIdentifier())
+ return ConsumeNameSuffix(Name, Node.getName());
+
if (Node.getDeclName()) {
// Name needs to be constructed.
llvm::SmallString<128> NodeName;
llvm::raw_svector_ostream OS(NodeName);
Node.printName(OS);
- return Name == OS.str();
+ return ConsumeNameSuffix(Name, OS.str());
}
- return false;
+
+ return ConsumeNameSuffix(Name, "(anonymous)");
+}
+
+} // namespace
+
+bool HasNameMatcher::matchesNodeUnqualified(const NamedDecl &Node) const {
+ assert(UseUnqualifiedMatch);
+ StringRef NodeName = Name;
+ return ConsumeNodeName(NodeName, Node) && NodeName.empty();
}
-bool HasNameMatcher::matchesNodeFull(const NamedDecl &Node) const {
- llvm::SmallString<128> NodeName = StringRef("::");
- llvm::raw_svector_ostream OS(NodeName);
- Node.printQualifiedName(OS);
- const StringRef FullName = OS.str();
+bool HasNameMatcher::matchesNodeFullFast(const NamedDecl &Node) const {
+ // This function is copied and adapted from NamedDecl::printQualifiedName()
+ // By matching each part individually we optimize in a couple of ways:
+ // - We can exit early on the first failure.
+ // - We can skip inline/anonymous namespaces without another pass.
+ // - We print one name at a time, reducing the chance of overflowing the
+ // inlined space of the SmallString.
+ StringRef Pattern = Name;
+ const bool IsFullyQualified = Pattern.startswith("::");
+
+ // First, match the name.
+ if (!ConsumeNodeName(Pattern, Node))
+ return false;
+
+ // Try to match each declaration context.
+ // We are allowed to skip anonymous and inline namespaces if they don't match.
+ const DeclContext *Ctx = Node.getDeclContext();
+
+ if (Ctx->isFunctionOrMethod())
+ return Pattern.empty() && !IsFullyQualified;
+
+ for (; !Pattern.empty() && Ctx && isa<NamedDecl>(Ctx);
+ Ctx = Ctx->getParent()) {
+ if (const auto *ND = dyn_cast<NamespaceDecl>(Ctx)) {
+ StringRef NSName =
+ ND->isAnonymousNamespace() ? "(anonymous namespace)" : ND->getName();
+
+ // If it matches, continue.
+ if (ConsumeNameSuffix(Pattern, NSName))
+ continue;
+ // If it didn't match but we can skip it, continue.
+ if (ND->isAnonymousNamespace() || ND->isInline())
+ continue;
+
+ return false;
+ }
+ if (const auto *RD = dyn_cast<RecordDecl>(Ctx)) {
+ if (!isa<ClassTemplateSpecializationDecl>(Ctx)) {
+ if (RD->getIdentifier()) {
+ if (ConsumeNameSuffix(Pattern, RD->getName()))
+ continue;
+ } else {
+ llvm::SmallString<128> NodeName;
+ NodeName += StringRef("(anonymous ");
+ NodeName += RD->getKindName();
+ NodeName += ')';
+ if (ConsumeNameSuffix(Pattern, NodeName))
+ continue;
+ }
+
+ return false;
+ }
+ }
+
+ // We don't know how to deal with this DeclContext.
+ // Fallback to the slow version of the code.
+ return matchesNodeFullSlow(Node);
+ }
+
+ // If we are fully qualified, we must not have any leftover context.
+ if (IsFullyQualified && Ctx && isa<NamedDecl>(Ctx))
+ return false;
+
+ return Pattern.empty();
+}
+
+bool HasNameMatcher::matchesNodeFullSlow(const NamedDecl &Node) const {
const StringRef Pattern = Name;
- if (Pattern.startswith("::"))
- return FullName == Pattern;
+ const bool SkipUnwrittenCases[] = {false, true};
+ for (bool SkipUnwritten : SkipUnwrittenCases) {
+ llvm::SmallString<128> NodeName = StringRef("::");
+ llvm::raw_svector_ostream OS(NodeName);
- return FullName.endswith(Pattern) &&
- FullName.drop_back(Pattern.size()).endswith("::");
+ if (SkipUnwritten) {
+ PrintingPolicy Policy = Node.getASTContext().getPrintingPolicy();
+ Policy.SuppressUnwrittenScope = true;
+ Node.printQualifiedName(OS, Policy);
+ } else {
+ Node.printQualifiedName(OS);
+ }
+
+ const StringRef FullName = OS.str();
+
+ if (Pattern.startswith("::")) {
+ if (FullName == Pattern)
+ return true;
+ } else if (FullName.endswith(Pattern) &&
+ FullName.drop_back(Pattern.size()).endswith("::")) {
+ return true;
+ }
+ }
+
+ return false;
}
bool HasNameMatcher::matchesNode(const NamedDecl &Node) const {
- // FIXME: There is still room for improvement, but it would require copying a
- // lot of the logic from NamedDecl::printQualifiedName(). The benchmarks do
- // not show like that extra complexity is needed right now.
+ assert(matchesNodeFullFast(Node) == matchesNodeFullSlow(Node));
if (UseUnqualifiedMatch) {
- assert(matchesNodeUnqualified(Node) == matchesNodeFull(Node));
+ assert(matchesNodeUnqualified(Node) == matchesNodeFullFast(Node));
return matchesNodeUnqualified(Node);
}
- return matchesNodeFull(Node);
+ return matchesNodeFullFast(Node);
}
} // end namespace internal
Modified: cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp?rev=259898&r1=259897&r2=259898&view=diff
==============================================================================
--- cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp (original)
+++ cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp Fri Feb 5 12:29:24 2016
@@ -2827,6 +2827,52 @@ TEST(Matcher, HasNameSupportsOuterClasse
recordDecl(hasName("A+B::C"))));
}
+TEST(Matcher, HasNameSupportsInlinedNamespaces) {
+ std::string code = "namespace a { inline namespace b { class C; } }";
+ EXPECT_TRUE(matches(code, recordDecl(hasName("a::b::C"))));
+ EXPECT_TRUE(matches(code, recordDecl(hasName("a::C"))));
+ EXPECT_TRUE(matches(code, recordDecl(hasName("::a::b::C"))));
+ EXPECT_TRUE(matches(code, recordDecl(hasName("::a::C"))));
+}
+
+TEST(Matcher, HasNameSupportsAnonymousNamespaces) {
+ std::string code = "namespace a { namespace { class C; } }";
+ EXPECT_TRUE(
+ matches(code, recordDecl(hasName("a::(anonymous namespace)::C"))));
+ EXPECT_TRUE(matches(code, recordDecl(hasName("a::C"))));
+ EXPECT_TRUE(
+ matches(code, recordDecl(hasName("::a::(anonymous namespace)::C"))));
+ EXPECT_TRUE(matches(code, recordDecl(hasName("::a::C"))));
+}
+
+TEST(Matcher, HasNameSupportsAnonymousOuterClasses) {
+ EXPECT_TRUE(matches("class A { class { class C; } x; };",
+ recordDecl(hasName("A::(anonymous class)::C"))));
+ EXPECT_TRUE(matches("class A { class { class C; } x; };",
+ recordDecl(hasName("::A::(anonymous class)::C"))));
+ EXPECT_FALSE(matches("class A { class { class C; } x; };",
+ recordDecl(hasName("::A::C"))));
+ EXPECT_TRUE(matches("class A { struct { class C; } x; };",
+ recordDecl(hasName("A::(anonymous struct)::C"))));
+ EXPECT_TRUE(matches("class A { struct { class C; } x; };",
+ recordDecl(hasName("::A::(anonymous struct)::C"))));
+ EXPECT_FALSE(matches("class A { struct { class C; } x; };",
+ recordDecl(hasName("::A::C"))));
+}
+
+TEST(Matcher, HasNameSupportsFunctionScope) {
+ std::string code =
+ "namespace a { void F(int a) { struct S { int m; }; int i; } }";
+ EXPECT_TRUE(matches(code, varDecl(hasName("i"))));
+ EXPECT_FALSE(matches(code, varDecl(hasName("F()::i"))));
+
+ EXPECT_TRUE(matches(code, fieldDecl(hasName("m"))));
+ EXPECT_TRUE(matches(code, fieldDecl(hasName("S::m"))));
+ EXPECT_TRUE(matches(code, fieldDecl(hasName("F(int)::S::m"))));
+ EXPECT_TRUE(matches(code, fieldDecl(hasName("a::F(int)::S::m"))));
+ EXPECT_TRUE(matches(code, fieldDecl(hasName("::a::F(int)::S::m"))));
+}
+
TEST(Matcher, IsDefinition) {
DeclarationMatcher DefinitionOfClassA =
recordDecl(hasName("A"), isDefinition());
More information about the cfe-commits
mailing list