[clang] 4604db9 - [ASTStructuralEquivalence] Add support for comparing ObjCCategoryDecl.
Volodymyr Sapsai via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 22 16:51:38 PDT 2022
Author: Volodymyr Sapsai
Date: 2022-04-22T16:51:19-07:00
New Revision: 4604db9493ffb22e1f411d907f163bf021193d84
URL: https://github.com/llvm/llvm-project/commit/4604db9493ffb22e1f411d907f163bf021193d84
DIFF: https://github.com/llvm/llvm-project/commit/4604db9493ffb22e1f411d907f163bf021193d84.diff
LOG: [ASTStructuralEquivalence] Add support for comparing ObjCCategoryDecl.
Differential Revision: https://reviews.llvm.org/D121176
Added:
Modified:
clang/include/clang/Testing/CommandLineArgs.h
clang/lib/AST/ASTStructuralEquivalence.cpp
clang/lib/Testing/CommandLineArgs.cpp
clang/unittests/AST/MatchVerifier.h
clang/unittests/AST/StructuralEquivalenceTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Testing/CommandLineArgs.h b/clang/include/clang/Testing/CommandLineArgs.h
index fe2103a3dce21..e668781ee2ce1 100644
--- a/clang/include/clang/Testing/CommandLineArgs.h
+++ b/clang/include/clang/Testing/CommandLineArgs.h
@@ -29,6 +29,7 @@ enum TestLanguage {
Lang_CXX17,
Lang_CXX20,
Lang_OpenCL,
+ Lang_OBJC,
Lang_OBJCXX
};
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 05f3470a179d2..b15036a11ad92 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -1242,10 +1242,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return true;
}
-/// Determine structural equivalence of two fields.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
- FieldDecl *Field1, FieldDecl *Field2) {
- const auto *Owner2 = cast<RecordDecl>(Field2->getDeclContext());
+ FieldDecl *Field1, FieldDecl *Field2,
+ QualType Owner2Type) {
+ const auto *Owner2 = cast<Decl>(Field2->getDeclContext());
// For anonymous structs/unions, match up the anonymous struct/union type
// declarations directly, so that we don't go off searching for anonymous
@@ -1265,7 +1265,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Context.Diag2(
Owner2->getLocation(),
Context.getApplicableDiagnostic(diag::err_odr_tag_type_inconsistent))
- << Context.ToCtx.getTypeDeclType(Owner2);
+ << Owner2Type;
Context.Diag2(Field2->getLocation(), diag::note_odr_field_name)
<< Field2->getDeclName();
Context.Diag1(Field1->getLocation(), diag::note_odr_field_name)
@@ -1280,7 +1280,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Context.Diag2(
Owner2->getLocation(),
Context.getApplicableDiagnostic(diag::err_odr_tag_type_inconsistent))
- << Context.ToCtx.getTypeDeclType(Owner2);
+ << Owner2Type;
Context.Diag2(Field2->getLocation(), diag::note_odr_field)
<< Field2->getDeclName() << Field2->getType();
Context.Diag1(Field1->getLocation(), diag::note_odr_field)
@@ -1296,6 +1296,14 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return true;
}
+/// Determine structural equivalence of two fields.
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+ FieldDecl *Field1, FieldDecl *Field2) {
+ const auto *Owner2 = cast<RecordDecl>(Field2->getDeclContext());
+ return IsStructurallyEquivalent(Context, Field1, Field2,
+ Context.ToCtx.getTypeDeclType(Owner2));
+}
+
/// Determine structural equivalence of two methods.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
CXXMethodDecl *Method1,
@@ -1610,6 +1618,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
}
// Check the fields for consistency.
+ QualType D2Type = Context.ToCtx.getTypeDeclType(D2);
RecordDecl::field_iterator Field2 = D2->field_begin(),
Field2End = D2->field_end();
for (RecordDecl::field_iterator Field1 = D1->field_begin(),
@@ -1628,7 +1637,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
}
- if (!IsStructurallyEquivalent(Context, *Field1, *Field2))
+ if (!IsStructurallyEquivalent(Context, *Field1, *Field2, D2Type))
return false;
}
@@ -1934,6 +1943,126 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return true;
}
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+ ObjCIvarDecl *D1, ObjCIvarDecl *D2,
+ QualType Owner2Type) {
+ if (D1->getAccessControl() != D2->getAccessControl())
+ return false;
+
+ return IsStructurallyEquivalent(Context, cast<FieldDecl>(D1),
+ cast<FieldDecl>(D2), Owner2Type);
+}
+
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+ ObjCIvarDecl *D1, ObjCIvarDecl *D2) {
+ QualType Owner2Type =
+ Context.ToCtx.getObjCInterfaceType(D2->getContainingInterface());
+ return IsStructurallyEquivalent(Context, D1, D2, Owner2Type);
+}
+
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+ ObjCMethodDecl *Method1,
+ ObjCMethodDecl *Method2) {
+ bool PropertiesEqual =
+ Method1->isInstanceMethod() == Method2->isInstanceMethod() &&
+ Method1->isVariadic() == Method2->isVariadic() &&
+ Method1->isDirectMethod() == Method2->isDirectMethod();
+ if (!PropertiesEqual)
+ return false;
+
+ // Compare selector slot names.
+ Selector Selector1 = Method1->getSelector(),
+ Selector2 = Method2->getSelector();
+ unsigned NumArgs = Selector1.getNumArgs();
+ if (NumArgs != Selector2.getNumArgs())
+ return false;
+ // Compare all selector slots. For selectors with arguments it means all arg
+ // slots. And if there are no arguments, compare the first-and-only slot.
+ unsigned SlotsToCheck = NumArgs > 0 ? NumArgs : 1;
+ for (unsigned I = 0; I < SlotsToCheck; ++I) {
+ if (!IsStructurallyEquivalent(Selector1.getIdentifierInfoForSlot(I),
+ Selector2.getIdentifierInfoForSlot(I)))
+ return false;
+ }
+
+ // Compare types.
+ if (!IsStructurallyEquivalent(Context, Method1->getReturnType(),
+ Method2->getReturnType()))
+ return false;
+ assert(
+ Method1->param_size() == Method2->param_size() &&
+ "Same number of arguments should be already enforced in Selector checks");
+ for (ObjCMethodDecl::param_type_iterator
+ ParamT1 = Method1->param_type_begin(),
+ ParamT1End = Method1->param_type_end(),
+ ParamT2 = Method2->param_type_begin(),
+ ParamT2End = Method2->param_type_end();
+ (ParamT1 != ParamT1End) && (ParamT2 != ParamT2End);
+ ++ParamT1, ++ParamT2) {
+ if (!IsStructurallyEquivalent(Context, *ParamT1, *ParamT2))
+ return false;
+ }
+
+ return true;
+}
+
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+ ObjCCategoryDecl *D1,
+ ObjCCategoryDecl *D2) {
+ if (!IsStructurallyEquivalent(D1->getIdentifier(), D2->getIdentifier()))
+ return false;
+
+ if (!IsStructurallyEquivalent(D1->getClassInterface()->getIdentifier(),
+ D2->getClassInterface()->getIdentifier()))
+ return false;
+
+ // Compare protocols.
+ ObjCCategoryDecl::protocol_iterator Protocol2 = D2->protocol_begin(),
+ Protocol2End = D2->protocol_end();
+ for (ObjCCategoryDecl::protocol_iterator Protocol1 = D1->protocol_begin(),
+ Protocol1End = D1->protocol_end();
+ Protocol1 != Protocol1End; ++Protocol1, ++Protocol2) {
+ if (Protocol2 == Protocol2End)
+ return false;
+ if (!IsStructurallyEquivalent((*Protocol1)->getIdentifier(),
+ (*Protocol2)->getIdentifier()))
+ return false;
+ }
+ if (Protocol2 != Protocol2End)
+ return false;
+
+ // Compare ivars.
+ QualType D2Type = Context.ToCtx.getObjCInterfaceType(D2->getClassInterface());
+ ObjCCategoryDecl::ivar_iterator Ivar2 = D2->ivar_begin(),
+ Ivar2End = D2->ivar_end();
+ for (ObjCCategoryDecl::ivar_iterator Ivar1 = D1->ivar_begin(),
+ Ivar1End = D1->ivar_end();
+ Ivar1 != Ivar1End; ++Ivar1, ++Ivar2) {
+ if (Ivar2 == Ivar2End)
+ return false;
+ if (!IsStructurallyEquivalent(Context, *Ivar1, *Ivar2, D2Type))
+ return false;
+ }
+ if (Ivar2 != Ivar2End)
+ return false;
+
+ // Compare methods.
+ ObjCCategoryDecl::method_iterator Method2 = D2->meth_begin(),
+ Method2End = D2->meth_end();
+ for (ObjCCategoryDecl::method_iterator Method1 = D1->meth_begin(),
+ Method1End = D1->meth_end();
+ Method1 != Method1End; ++Method1, ++Method2) {
+ if (Method2 == Method2End)
+ return false;
+ if (!IsStructurallyEquivalent(Context, *Method1, *Method2))
+ return false;
+ }
+ if (Method2 != Method2End)
+ return false;
+
+ return true;
+}
+
/// Determine structural equivalence of two declarations.
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Decl *D1, Decl *D2) {
diff --git a/clang/lib/Testing/CommandLineArgs.cpp b/clang/lib/Testing/CommandLineArgs.cpp
index d71b9361f9589..c04702f689028 100644
--- a/clang/lib/Testing/CommandLineArgs.cpp
+++ b/clang/lib/Testing/CommandLineArgs.cpp
@@ -36,6 +36,9 @@ std::vector<std::string> getCommandLineArgsForTesting(TestLanguage Lang) {
case Lang_CXX20:
Args = {"-std=c++20", "-frtti"};
break;
+ case Lang_OBJC:
+ Args = {"-x", "objective-c", "-frtti", "-fobjc-nonfragile-abi"};
+ break;
case Lang_OBJCXX:
Args = {"-x", "objective-c++", "-frtti"};
break;
@@ -94,6 +97,9 @@ StringRef getFilenameForTesting(TestLanguage Lang) {
case Lang_OpenCL:
return "input.cl";
+ case Lang_OBJC:
+ return "input.m";
+
case Lang_OBJCXX:
return "input.mm";
}
diff --git a/clang/unittests/AST/MatchVerifier.h b/clang/unittests/AST/MatchVerifier.h
index c49a2f8221b42..8bcf05642cb5b 100644
--- a/clang/unittests/AST/MatchVerifier.h
+++ b/clang/unittests/AST/MatchVerifier.h
@@ -120,6 +120,10 @@ MatchVerifier<NodeType>::match(const std::string &Code,
Args.push_back("-cl-no-stdinc");
FileName = "input.cl";
break;
+ case Lang_OBJC:
+ Args.push_back("-fobjc-nonfragile-abi");
+ FileName = "input.m";
+ break;
case Lang_OBJCXX:
FileName = "input.mm";
break;
diff --git a/clang/unittests/AST/StructuralEquivalenceTest.cpp b/clang/unittests/AST/StructuralEquivalenceTest.cpp
index a1b506c3c28d9..9ddc460d1a094 100644
--- a/clang/unittests/AST/StructuralEquivalenceTest.cpp
+++ b/clang/unittests/AST/StructuralEquivalenceTest.cpp
@@ -1120,6 +1120,187 @@ TEST_F(StructuralEquivalenceEnumConstantTest, EnumConstantsWithDifferentName) {
EXPECT_FALSE(testStructuralMatch(t));
}
+struct StructuralEquivalenceObjCCategoryTest : StructuralEquivalenceTest {};
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, MatchinCategoryNames) {
+ auto t = makeDecls<ObjCCategoryDecl>("@interface A @end @interface A(X) @end",
+ "@interface A @end @interface A(X) @end",
+ Lang_OBJC, objcCategoryDecl());
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, CategoriesForDifferentClasses) {
+ auto t = makeDecls<ObjCCategoryDecl>("@interface A @end @interface A(X) @end",
+ "@interface B @end @interface B(X) @end",
+ Lang_OBJC, objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, CategoriesWithDifferentNames) {
+ auto t = makeDecls<ObjCCategoryDecl>("@interface A @end @interface A(X) @end",
+ "@interface A @end @interface A(Y) @end",
+ Lang_OBJC, objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, CategoryAndExtension) {
+ auto t = makeDecls<ObjCCategoryDecl>("@interface A @end @interface A(X) @end",
+ "@interface A @end @interface A() @end",
+ Lang_OBJC, objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, MatchingProtocols) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@protocol P @end @interface A @end @interface A(X)<P> @end",
+ "@protocol P @end @interface A @end @interface A(X)<P> @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentProtocols) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@protocol P @end @interface A @end @interface A(X)<P> @end",
+ "@protocol Q @end @interface A @end @interface A(X)<Q> @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentProtocolsOrder) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@protocol P @end @protocol Q @end @interface A @end @interface A(X)<P, "
+ "Q> @end",
+ "@protocol P @end @protocol Q @end @interface A @end @interface A(X)<Q, "
+ "P> @end",
+ Lang_OBJC, objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, MatchingIvars) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A() { int x; } @end",
+ "@interface A @end @interface A() { int x; } @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentIvarName) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A() { int x; } @end",
+ "@interface A @end @interface A() { int y; } @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentIvarType) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A() { int x; } @end",
+ "@interface A @end @interface A() { float x; } @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentIvarBitfieldWidth) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A() { int x: 1; } @end",
+ "@interface A @end @interface A() { int x: 2; } @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentIvarVisibility) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A() { @public int x; } @end",
+ "@interface A @end @interface A() { @protected int x; } @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentIvarNumber) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A() { int x; } @end",
+ "@interface A @end @interface A() {} @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentIvarOrder) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A() { int x; int y; } @end",
+ "@interface A @end @interface A() { int y; int x; } @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, MatchingMethods) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A(X) -(void)test; @end",
+ "@interface A @end @interface A(X) -(void)test; @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodName) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A(X) -(void)test; @end",
+ "@interface A @end @interface A(X) -(void)wasd; @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+
+ auto t2 = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A(X) -(void)test:(int)x more:(int)y; @end",
+ "@interface A @end @interface A(X) -(void)test:(int)x :(int)y; @end",
+ Lang_OBJC, objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t2));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodClassInstance) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A(X) -(void)test; @end",
+ "@interface A @end @interface A(X) +(void)test; @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodReturnType) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A(X) -(void)test; @end",
+ "@interface A @end @interface A(X) -(int)test; @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodParameterType) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A(X) -(void)test:(int)x; @end",
+ "@interface A @end @interface A(X) -(void)test:(float)x; @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodParameterName) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A(X) -(void)test:(int)x; @end",
+ "@interface A @end @interface A(X) -(void)test:(int)y; @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_TRUE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodNumber) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A(X) -(void)test; @end",
+ "@interface A @end @interface A(X) @end", Lang_OBJC, objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
+TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodOrder) {
+ auto t = makeDecls<ObjCCategoryDecl>(
+ "@interface A @end @interface A(X) -(void)u; -(void)v; @end",
+ "@interface A @end @interface A(X) -(void)v; -(void)u; @end", Lang_OBJC,
+ objcCategoryDecl());
+ EXPECT_FALSE(testStructuralMatch(t));
+}
+
struct StructuralEquivalenceTemplateTest : StructuralEquivalenceTest {};
TEST_F(StructuralEquivalenceTemplateTest, ExactlySameTemplates) {
More information about the cfe-commits
mailing list