forked from OSchip/llvm-project
[ASTStructuralEquivalence] Add support for comparing ObjCCategoryDecl.
Differential Revision: https://reviews.llvm.org/D121176
This commit is contained in:
parent
2a04f5c455
commit
4604db9493
|
@ -29,6 +29,7 @@ enum TestLanguage {
|
|||
Lang_CXX17,
|
||||
Lang_CXX20,
|
||||
Lang_OpenCL,
|
||||
Lang_OBJC,
|
||||
Lang_OBJCXX
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue