[clang] 6b96b2a - [clang][AST] Check context of record in structural equivalence.
Balázs Kéri via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 24 08:20:18 PST 2021
Author: Balázs Kéri
Date: 2021-11-24T17:36:15+01:00
New Revision: 6b96b2a0bf65ff838d4dbf909a5120d4d1083e29
URL: https://github.com/llvm/llvm-project/commit/6b96b2a0bf65ff838d4dbf909a5120d4d1083e29
DIFF: https://github.com/llvm/llvm-project/commit/6b96b2a0bf65ff838d4dbf909a5120d4d1083e29.diff
LOG: [clang][AST] Check context of record in structural equivalence.
The AST structural equivalence check did not differentiate between
a struct and a struct with same name in different namespace. When
type of a member is checked it is possible to encounter such a case
and wrongly decide that the types are similar. This problem is fixed
by check for the namespaces of a record declaration.
Reviewed By: martong
Differential Revision: https://reviews.llvm.org/D113118
Added:
Modified:
clang/lib/AST/ASTStructuralEquivalence.cpp
clang/unittests/AST/StructuralEquivalenceTest.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index e85feb779190f..7fd24e2aa9ad2 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -1347,6 +1347,42 @@ IsStructurallyEquivalentLambdas(StructuralEquivalenceContext &Context,
return true;
}
+/// Determine if context of a class is equivalent.
+static bool IsRecordContextStructurallyEquivalent(RecordDecl *D1,
+ RecordDecl *D2) {
+ // The context should be completely equal, including anonymous and inline
+ // namespaces.
+ // We compare objects as part of full translation units, not subtrees of
+ // translation units.
+ DeclContext *DC1 = D1->getDeclContext()->getNonTransparentContext();
+ DeclContext *DC2 = D2->getDeclContext()->getNonTransparentContext();
+ while (true) {
+ // Special case: We allow a struct defined in a function to be equivalent
+ // with a similar struct defined outside of a function.
+ if ((DC1->isFunctionOrMethod() && DC2->isTranslationUnit()) ||
+ (DC2->isFunctionOrMethod() && DC1->isTranslationUnit()))
+ return true;
+
+ if (DC1->getDeclKind() != DC2->getDeclKind())
+ return false;
+ if (DC1->isTranslationUnit())
+ break;
+ if (DC1->isInlineNamespace() != DC2->isInlineNamespace())
+ return false;
+ if (const auto *ND1 = dyn_cast<NamedDecl>(DC1)) {
+ const auto *ND2 = cast<NamedDecl>(DC2);
+ if (!DC1->isInlineNamespace() &&
+ !IsStructurallyEquivalent(ND1->getIdentifier(), ND2->getIdentifier()))
+ return false;
+ }
+
+ DC1 = DC1->getParent()->getNonTransparentContext();
+ DC2 = DC2->getParent()->getNonTransparentContext();
+ }
+
+ return true;
+}
+
/// Determine structural equivalence of two records.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
RecordDecl *D1, RecordDecl *D2) {
@@ -1386,6 +1422,12 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
}
}
+ // If the records occur in
diff erent context (namespace), these should be
+ //
diff erent. This is specially important if the definition of one or both
+ // records is missing.
+ if (!IsRecordContextStructurallyEquivalent(D1, D2))
+ return false;
+
// If both declarations are class template specializations, we know
// the ODR applies, so check the template and template arguments.
const auto *Spec1 = dyn_cast<ClassTemplateSpecializationDecl>(D1);
diff --git a/clang/unittests/AST/StructuralEquivalenceTest.cpp b/clang/unittests/AST/StructuralEquivalenceTest.cpp
index 9ae0da8b9dd2c..2f5b5c1460950 100644
--- a/clang/unittests/AST/StructuralEquivalenceTest.cpp
+++ b/clang/unittests/AST/StructuralEquivalenceTest.cpp
@@ -929,6 +929,136 @@ TEST_F(StructuralEquivalenceTest, ExplicitBoolSame) {
EXPECT_TRUE(testStructuralMatch(First, Second));
}
+struct StructuralEquivalenceRecordContextTest : StructuralEquivalenceTest {};
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceNoVsNamed) {
+ auto Decls = makeDecls<CXXRecordDecl>("class X;", "namespace N { class X; }",
+ Lang_CXX03, cxxRecordDecl());
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceNamedVsNamed) {
+ auto Decls = makeDecls<CXXRecordDecl>("namespace A { class X; }",
+ "namespace B { class X; }", Lang_CXX03,
+ cxxRecordDecl());
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceAnonVsNamed) {
+ auto Decls = makeDecls<CXXRecordDecl>("namespace { class X; }",
+ "namespace N { class X; }", Lang_CXX03,
+ cxxRecordDecl());
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceNoVsAnon) {
+ auto Decls = makeDecls<CXXRecordDecl>("class X;", "namespace { class X; }",
+ Lang_CXX03, cxxRecordDecl());
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceAnonVsAnon) {
+ auto Decls = makeDecls<CXXRecordDecl>("namespace { class X; }",
+ "namespace { class X; }", Lang_CXX03,
+ cxxRecordDecl());
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceAnonVsAnonAnon) {
+ auto Decls = makeDecls<CXXRecordDecl>("namespace { class X; }",
+ "namespace { namespace { class X; } }",
+ Lang_CXX03, cxxRecordDecl());
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest,
+ NamespaceNamedNamedVsNamedNamed) {
+ auto Decls = makeDecls<CXXRecordDecl>(
+ "namespace A { namespace N { class X; } }",
+ "namespace B { namespace N { class X; } }", Lang_CXX03, cxxRecordDecl());
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceNamedVsInline) {
+ auto Decls = makeDecls<CXXRecordDecl>(
+ "namespace A { namespace A { class X; } }",
+ "namespace A { inline namespace A { class X; } }", Lang_CXX17,
+ cxxRecordDecl());
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceInlineVsInline) {
+ auto Decls = makeDecls<CXXRecordDecl>(
+ "namespace A { inline namespace A { class X; } }",
+ "namespace A { inline namespace B { class X; } }", Lang_CXX17,
+ cxxRecordDecl());
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, NamespaceInlineTopLevel) {
+ auto Decls = makeDecls<CXXRecordDecl>("inline namespace A { class X; } }",
+ "inline namespace B { class X; } }",
+ Lang_CXX17, cxxRecordDecl());
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, TransparentContext) {
+ auto Decls = makeDecls<CXXRecordDecl>("extern \"C\" { class X; }", "class X;",
+ Lang_CXX03, cxxRecordDecl());
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, TransparentContextNE) {
+ auto Decls = makeDecls<CXXRecordDecl>("extern \"C\" { class X; }",
+ "namespace { class X; }", Lang_CXX03,
+ cxxRecordDecl());
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, TransparentContextInNamespace) {
+ auto Decls = makeDecls<CXXRecordDecl>(
+ "extern \"C\" { namespace N { class X; } }",
+ "namespace N { extern \"C\" { class X; } }", Lang_CXX03, cxxRecordDecl());
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, NamespaceOfRecordMember) {
+ auto Decls = makeDecls<CXXRecordDecl>(
+ R"(
+ class X;
+ class Y { X* x; };
+ )",
+ R"(
+ namespace N { class X; }
+ class Y { N::X* x; };
+ )",
+ Lang_CXX03, cxxRecordDecl(hasName("Y")));
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, StructDefinitionInPrototype) {
+ auto Decls = makeDecls<FunctionDecl>(
+ "struct Param { int a; }; void f(struct Param *p);",
+ "void f(struct Param { int a; } *p);", Lang_C89,
+ functionDecl(hasName("f")));
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceTest, StructDefinitionInPrototypeDifferentName) {
+ auto Decls = makeDecls<FunctionDecl>(
+ "struct Param1 { int a; }; void f(struct Param1 *p);",
+ "void f(struct Param2 { int a; } *p);", Lang_C89,
+ functionDecl(hasName("f")));
+ EXPECT_FALSE(testStructuralMatch(Decls));
+}
+
+TEST_F(StructuralEquivalenceRecordContextTest, RecordInsideFunction) {
+ auto Decls = makeDecls<RecordDecl>("struct Param { int a; };",
+ "void f() { struct Param { int a; }; }",
+ Lang_C89, recordDecl(hasName("Param")));
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
struct StructuralEquivalenceEnumTest : StructuralEquivalenceTest {};
TEST_F(StructuralEquivalenceEnumTest, FwdDeclEnumShouldBeEqualWithFwdDeclEnum) {
More information about the cfe-commits
mailing list