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) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits