[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