Fix bug in DynTypedMatcher::constructVariadic() that would cause false negatives.
Summary:
DynTypedMatcher::constructVariadic() where the restrict kind of the
different matchers are not related causes the matcher to have a "None"
restrict kind. This causes false negatives for anyOf and eachOf.
Change the logic to get a common ancestor if there is one.
Also added regression tests that fail without the fix.
Reviewers: klimek
Subscribers: klimek, cfe-commits
Differential Revision: http://reviews.llvm.org/D5580
llvm-svn: 219118
diff --git a/clang/include/clang/AST/ASTTypeTraits.h b/clang/include/clang/AST/ASTTypeTraits.h
index 01c5cec..efeac56 100644
--- a/clang/include/clang/AST/ASTTypeTraits.h
+++ b/clang/include/clang/AST/ASTTypeTraits.h
@@ -63,6 +63,9 @@
/// \brief Returns \c true if \c this and \c Other represent the same kind.
bool isSame(ASTNodeKind Other) const;
+ /// \brief Returns \c true only for the default \c ASTNodeKind()
+ bool isNone() const { return KindId == NKI_None; }
+
/// \brief Returns \c true if \c this is a base kind of (or same as) \c Other.
/// \param Distance If non-null, used to return the distance between \c this
/// and \c Other in the class hierarchy.
@@ -76,6 +79,17 @@
return KindId < Other.KindId;
}
+ /// \brief Return the most derived type between \p Kind1 and \p Kind2.
+ ///
+ /// Return ASTNodeKind() if they are not related.
+ static ASTNodeKind getMostDerivedType(ASTNodeKind Kind1, ASTNodeKind Kind2);
+
+ /// \brief Return the most derived common ancestor between Kind1 and Kind2.
+ ///
+ /// Return ASTNodeKind() if they are not related.
+ static ASTNodeKind getMostDerivedCommonAncestor(ASTNodeKind Kind1,
+ ASTNodeKind Kind2);
+
private:
/// \brief Kind ids.
///
diff --git a/clang/lib/AST/ASTTypeTraits.cpp b/clang/lib/AST/ASTTypeTraits.cpp
index 56915b9..ec0671c 100644
--- a/clang/lib/AST/ASTTypeTraits.cpp
+++ b/clang/lib/AST/ASTTypeTraits.cpp
@@ -62,6 +62,22 @@
StringRef ASTNodeKind::asStringRef() const { return AllKindInfo[KindId].Name; }
+ASTNodeKind ASTNodeKind::getMostDerivedType(ASTNodeKind Kind1,
+ ASTNodeKind Kind2) {
+ if (Kind1.isBaseOf(Kind2)) return Kind2;
+ if (Kind2.isBaseOf(Kind1)) return Kind1;
+ return ASTNodeKind();
+}
+
+ASTNodeKind ASTNodeKind::getMostDerivedCommonAncestor(ASTNodeKind Kind1,
+ ASTNodeKind Kind2) {
+ NodeKindId Parent = Kind1.KindId;
+ while (!isBaseOf(Parent, Kind2.KindId, nullptr) && Parent != NKI_None) {
+ Parent = AllKindInfo[Parent].ParentId;
+ }
+ return ASTNodeKind(Parent);
+}
+
ASTNodeKind ASTNodeKind::getFromNode(const Decl &D) {
switch (D.getKind()) {
#define DECL(DERIVED, BASE) \
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 4cff381..ec60f0d 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -64,28 +64,6 @@
const IntrusiveRefCntPtr<DynMatcherInterface> InnerMatcher;
};
-/// \brief Return the most derived type between \p Kind1 and \p Kind2.
-///
-/// Return the null type if they are not related.
-ast_type_traits::ASTNodeKind getMostDerivedType(
- const ast_type_traits::ASTNodeKind Kind1,
- const ast_type_traits::ASTNodeKind Kind2) {
- if (Kind1.isBaseOf(Kind2)) return Kind2;
- if (Kind2.isBaseOf(Kind1)) return Kind1;
- return ast_type_traits::ASTNodeKind();
-}
-
-/// \brief Return the least derived type between \p Kind1 and \p Kind2.
-///
-/// Return the null type if they are not related.
-static ast_type_traits::ASTNodeKind getLeastDerivedType(
- const ast_type_traits::ASTNodeKind Kind1,
- const ast_type_traits::ASTNodeKind Kind2) {
- if (Kind1.isBaseOf(Kind2)) return Kind1;
- if (Kind2.isBaseOf(Kind1)) return Kind2;
- return ast_type_traits::ASTNodeKind();
-}
-
} // namespace
DynTypedMatcher DynTypedMatcher::constructVariadic(
@@ -98,7 +76,8 @@
assert(Result.SupportedKind.isSame(M.SupportedKind) &&
"SupportedKind must match!");
Result.RestrictKind =
- getLeastDerivedType(Result.RestrictKind, M.RestrictKind);
+ ast_type_traits::ASTNodeKind::getMostDerivedCommonAncestor(
+ Result.RestrictKind, M.RestrictKind);
}
Result.Implementation = new VariadicMatcher(Func, std::move(InnerMatchers));
return Result;
@@ -108,7 +87,8 @@
const ast_type_traits::ASTNodeKind Kind) const {
auto Copy = *this;
Copy.SupportedKind = Kind;
- Copy.RestrictKind = getMostDerivedType(Kind, RestrictKind);
+ Copy.RestrictKind =
+ ast_type_traits::ASTNodeKind::getMostDerivedType(Kind, RestrictKind);
return Copy;
}
diff --git a/clang/unittests/AST/ASTTypeTraitsTest.cpp b/clang/unittests/AST/ASTTypeTraitsTest.cpp
index 0e73f76..eeb01cc 100644
--- a/clang/unittests/AST/ASTTypeTraitsTest.cpp
+++ b/clang/unittests/AST/ASTTypeTraitsTest.cpp
@@ -26,6 +26,12 @@
return ASTNodeKind::getFromNodeKind<T>();
}
+TEST(ASTNodeKind, IsNone) {
+ EXPECT_TRUE(ASTNodeKind().isNone());
+ EXPECT_FALSE(DNT<Decl>().isNone());
+ EXPECT_FALSE(DNT<VarDecl>().isNone());
+}
+
TEST(ASTNodeKind, Bases) {
EXPECT_TRUE(DNT<Decl>().isBaseOf(DNT<VarDecl>()));
EXPECT_FALSE(DNT<Decl>().isSame(DNT<VarDecl>()));
@@ -60,6 +66,39 @@
EXPECT_FALSE(DNT<Type>().isSame(DNT<QualType>()));
}
+TEST(ASTNodeKind, MostDerivedType) {
+ EXPECT_TRUE(DNT<BinaryOperator>().isSame(
+ ASTNodeKind::getMostDerivedType(DNT<Expr>(), DNT<BinaryOperator>())));
+ EXPECT_TRUE(DNT<BinaryOperator>().isSame(
+ ASTNodeKind::getMostDerivedType(DNT<BinaryOperator>(), DNT<Expr>())));
+ EXPECT_TRUE(DNT<VarDecl>().isSame(
+ ASTNodeKind::getMostDerivedType(DNT<VarDecl>(), DNT<VarDecl>())));
+
+ // Not related. Returns nothing.
+ EXPECT_TRUE(
+ ASTNodeKind::getMostDerivedType(DNT<IfStmt>(), DNT<VarDecl>()).isNone());
+ EXPECT_TRUE(ASTNodeKind::getMostDerivedType(DNT<IfStmt>(),
+ DNT<BinaryOperator>()).isNone());
+}
+
+TEST(ASTNodeKind, MostDerivedCommonAncestor) {
+ EXPECT_TRUE(DNT<Expr>().isSame(ASTNodeKind::getMostDerivedCommonAncestor(
+ DNT<Expr>(), DNT<BinaryOperator>())));
+ EXPECT_TRUE(DNT<Expr>().isSame(ASTNodeKind::getMostDerivedCommonAncestor(
+ DNT<BinaryOperator>(), DNT<Expr>())));
+ EXPECT_TRUE(DNT<VarDecl>().isSame(ASTNodeKind::getMostDerivedCommonAncestor(
+ DNT<VarDecl>(), DNT<VarDecl>())));
+
+ // A little related. Returns the ancestor.
+ EXPECT_TRUE(
+ DNT<NamedDecl>().isSame(ASTNodeKind::getMostDerivedCommonAncestor(
+ DNT<CXXMethodDecl>(), DNT<RecordDecl>())));
+
+ // Not related. Returns nothing.
+ EXPECT_TRUE(ASTNodeKind::getMostDerivedCommonAncestor(
+ DNT<IfStmt>(), DNT<VarDecl>()).isNone());
+}
+
struct Foo {};
TEST(ASTNodeKind, UnknownKind) {
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTest.cpp
index 4eb4c0d..a796934 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -460,6 +460,11 @@
EXPECT_TRUE(matches("class U {};", XOrYOrZOrUOrV));
EXPECT_TRUE(matches("class V {};", XOrYOrZOrUOrV));
EXPECT_TRUE(notMatches("class A {};", XOrYOrZOrUOrV));
+
+ StatementMatcher MixedTypes = stmt(anyOf(ifStmt(), binaryOperator()));
+ EXPECT_TRUE(matches("int F() { return 1 + 2; }", MixedTypes));
+ EXPECT_TRUE(matches("int F() { if (true) return 1; }", MixedTypes));
+ EXPECT_TRUE(notMatches("int F() { return 1; }", MixedTypes));
}
TEST(DeclarationMatcher, MatchHas) {
diff --git a/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp b/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp
index f8a9430..824b310 100644
--- a/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp
+++ b/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp
@@ -347,7 +347,7 @@
"anyOf",
constructMatcher("recordDecl",
constructMatcher("hasName", std::string("Foo"))),
- constructMatcher("namedDecl",
+ constructMatcher("functionDecl",
constructMatcher("hasName", std::string("foo"))))
.getTypedMatcher<Decl>();