[ASTStructuralEquivalence] Add support for comparing ObjCCategoryDecl.

Differential Revision: https://reviews.llvm.org/D121176
This commit is contained in:
Volodymyr Sapsai 2022-03-04 17:33:48 -08:00
parent 2a04f5c455
commit 4604db9493
5 changed files with 327 additions and 6 deletions

View File

@ -29,6 +29,7 @@ enum TestLanguage {
Lang_CXX17,
Lang_CXX20,
Lang_OpenCL,
Lang_OBJC,
Lang_OBJCXX
};

View File

@ -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) {

View File

@ -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";
}

View File

@ -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;

View File

@ -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) {