diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -14,6 +14,7 @@ #include "ASTCommon.h" #include "ASTReaderInternals.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/ASTStructuralEquivalence.h" #include "clang/AST/Attr.h" #include "clang/AST/AttrIterator.h" #include "clang/AST/Decl.h" @@ -4181,23 +4182,22 @@ // Check for duplicate categories. if (Cat->getDeclName()) { ObjCCategoryDecl *&Existing = NameCategoryMap[Cat->getDeclName()]; - if (Existing && - Reader.getOwningModuleFile(Existing) - != Reader.getOwningModuleFile(Cat)) { - // FIXME: We should not warn for duplicates in diamond: - // - // MT // - // / \ // - // ML MR // - // \ / // - // MB // - // - // If there are duplicates in ML/MR, there will be warning when - // creating MB *and* when importing MB. We should not warn when - // importing. - Reader.Diag(Cat->getLocation(), diag::warn_dup_category_def) - << Interface->getDeclName() << Cat->getDeclName(); - Reader.Diag(Existing->getLocation(), diag::note_previous_definition); + if (Existing && Reader.getOwningModuleFile(Existing) != + Reader.getOwningModuleFile(Cat)) { + llvm::DenseSet> NonEquivalentDecls; + StructuralEquivalenceContext Ctx( + Cat->getASTContext(), Existing->getASTContext(), + NonEquivalentDecls, StructuralEquivalenceKind::Default, + /*StrictTypeSpelling =*/false, + /*Complain =*/false, + /*ErrorOnTagTypeMismatch =*/true); + if (!Ctx.IsEquivalent(Cat, Existing)) { + // Warn only if the categories with the same name are different. + Reader.Diag(Cat->getLocation(), diag::warn_dup_category_def) + << Interface->getDeclName() << Cat->getDeclName(); + Reader.Diag(Existing->getLocation(), + diag::note_previous_definition); + } } else if (!Existing) { // Record this category. Existing = Cat; diff --git a/clang/test/Modules/compare-objc-interface.m b/clang/test/Modules/compare-objc-interface.m --- a/clang/test/Modules/compare-objc-interface.m +++ b/clang/test/Modules/compare-objc-interface.m @@ -444,3 +444,54 @@ // expected-error@first.h:* {{'CompareLastImplAttribute' has different definitions in different modules; first difference is definition in module 'First.Hidden' found property 'lastImplAttribute' with 'direct' attribute}} // expected-note-re@second.h:* {{but in {{'Second'|definition here}} found property 'lastImplAttribute' with different 'direct' attribute}} #endif + +#if defined(FIRST) +@interface CompareMatchingCategories: NSObject @end +@interface CompareMatchingCategories(Matching) +- (int)testMethod; +@end + +@interface CompareMismatchingCategories1: NSObject @end +@interface CompareMismatchingCategories1(Category1) +- (void)presentMethod; +@end +@interface CompareMismatchingCategories2: NSObject @end +@interface CompareMismatchingCategories2(Category2) +@end + +@interface CompareDifferentCategoryNames: NSObject @end +@interface CompareDifferentCategoryNames(CategoryFirst) +- (void)firstMethod:(int)x; +@end +#elif defined(SECOND) +@interface CompareMatchingCategories: NSObject @end +@interface CompareMatchingCategories(Matching) +- (int)testMethod; +@end + +@interface CompareMismatchingCategories1: NSObject @end +@interface CompareMismatchingCategories1(Category1) +@end +@interface CompareMismatchingCategories2: NSObject @end +@interface CompareMismatchingCategories2(Category2) +- (void)presentMethod; +@end + +@interface CompareDifferentCategoryNames: NSObject @end +@interface CompareDifferentCategoryNames(CategorySecond) +- (void)secondMethod; +@end +#else +CompareMatchingCategories *compareMatchingCategories; // no diagnostic +CompareMismatchingCategories1 *compareMismatchingCategories1; +#ifdef TEST_MODULAR +// expected-warning@second.h:* {{duplicate definition of category 'Category1' on interface 'CompareMismatchingCategories1'}} +// expected-note@first.h:* {{previous definition is here}} +#endif +CompareMismatchingCategories2 *compareMismatchingCategories2; +#ifdef TEST_MODULAR +// expected-warning@second.h:* {{duplicate definition of category 'Category2' on interface 'CompareMismatchingCategories2'}} +// expected-note@first.h:* {{previous definition is here}} +#endif +CompareDifferentCategoryNames *compareDifferentCategoryNames; // no diagnostic +#endif diff --git a/clang/test/Modules/hidden-duplicates.m b/clang/test/Modules/hidden-duplicates.m --- a/clang/test/Modules/hidden-duplicates.m +++ b/clang/test/Modules/hidden-duplicates.m @@ -32,6 +32,7 @@ @interface NSObject @end @class ForwardDeclaredInterfaceWithoutDefinition; +@interface NSObject(CategoryForTesting) @end NSObject *interfaceDefinition(NSObject *o); NSObject *forwardDeclaredInterface(NSObject *o); diff --git a/clang/test/Modules/objc-categories.m b/clang/test/Modules/objc-categories.m --- a/clang/test/Modules/objc-categories.m +++ b/clang/test/Modules/objc-categories.m @@ -8,8 +8,6 @@ @import category_bottom; -// expected-note@Inputs/category_left.h:14 {{previous definition}} -// expected-warning@Inputs/category_right.h:12 {{duplicate definition of category}} // expected-note@Inputs/category_top.h:1 {{receiver is instance of class declared here}} @interface Foo(Source)