[clang] 6ba4afb - [ODRHash] Hash `ObjCInterfaceDecl` and diagnose discovered mismatches.
Volodymyr Sapsai via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 20 08:18:26 PST 2023
Author: Volodymyr Sapsai
Date: 2023-01-20T10:18:18-06:00
New Revision: 6ba4afb4d6f2f8f293ad704a37de4139c5c8c0f0
URL: https://github.com/llvm/llvm-project/commit/6ba4afb4d6f2f8f293ad704a37de4139c5c8c0f0
DIFF: https://github.com/llvm/llvm-project/commit/6ba4afb4d6f2f8f293ad704a37de4139c5c8c0f0.diff
LOG: [ODRHash] Hash `ObjCInterfaceDecl` and diagnose discovered mismatches.
When two modules contain interfaces with the same name, check the
definitions are equivalent and diagnose if they are not.
Differential Revision: https://reviews.llvm.org/D140073
Added:
clang/test/Modules/compare-objc-interface.m
Modified:
clang/include/clang/AST/DeclObjC.h
clang/include/clang/AST/ODRDiagsEmitter.h
clang/include/clang/AST/ODRHash.h
clang/include/clang/Basic/DiagnosticASTKinds.td
clang/include/clang/Serialization/ASTReader.h
clang/lib/AST/DeclObjC.cpp
clang/lib/AST/ODRDiagsEmitter.cpp
clang/lib/AST/ODRHash.cpp
clang/lib/Serialization/ASTReader.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriter.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
clang/test/Modules/interface-diagnose-missing-import.m
clang/test/Modules/method_pool.m
Removed:
################################################################################
diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h
index 7c72ec9507bf1..77fde99b6b60b 100644
--- a/clang/include/clang/AST/DeclObjC.h
+++ b/clang/include/clang/AST/DeclObjC.h
@@ -1146,6 +1146,7 @@ class ObjCContainerDecl : public NamedDecl, public DeclContext {
class ObjCInterfaceDecl : public ObjCContainerDecl
, public Redeclarable<ObjCInterfaceDecl> {
friend class ASTContext;
+ friend class ODRDiagsEmitter;
/// TypeForDecl - This indicates the Type object that represents this
/// TypeDecl. It is a cache maintained by ASTContext::getObjCInterfaceType
@@ -1203,6 +1204,12 @@ class ObjCInterfaceDecl : public ObjCContainerDecl
/// One of the \c InheritedDesignatedInitializersState enumeratos.
mutable unsigned InheritedDesignatedInitializers : 2;
+ /// Tracks whether a ODR hash has been computed for this interface.
+ unsigned HasODRHash : 1;
+
+ /// A hash of parts of the class to help in ODR checking.
+ unsigned ODRHash = 0;
+
/// The location of the last location in this declaration, before
/// the properties/methods. For example, this will be the '>', '}', or
/// identifier,
@@ -1211,7 +1218,7 @@ class ObjCInterfaceDecl : public ObjCContainerDecl
DefinitionData()
: ExternallyCompleted(false), IvarListMissingImplementation(true),
HasDesignatedInitializers(false),
- InheritedDesignatedInitializers(IDI_Unknown) {}
+ InheritedDesignatedInitializers(IDI_Unknown), HasODRHash(false) {}
};
/// The type parameters associated with this class, if any.
@@ -1892,10 +1899,17 @@ class ObjCInterfaceDecl : public ObjCContainerDecl
const Type *getTypeForDecl() const { return TypeForDecl; }
void setTypeForDecl(const Type *TD) const { TypeForDecl = TD; }
+ /// Get precomputed ODRHash or add a new one.
+ unsigned getODRHash();
+
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == ObjCInterface; }
private:
+ /// True if a valid hash is stored in ODRHash.
+ bool hasODRHash() const;
+ void setHasODRHash(bool HasHash);
+
const ObjCInterfaceDecl *findInterfaceWithDesignatedInitializers() const;
bool inheritsDesignatedInitializers() const;
};
diff --git a/clang/include/clang/AST/ODRDiagsEmitter.h b/clang/include/clang/AST/ODRDiagsEmitter.h
index 00a681b78a5eb..fdbd85cb10e5b 100644
--- a/clang/include/clang/AST/ODRDiagsEmitter.h
+++ b/clang/include/clang/AST/ODRDiagsEmitter.h
@@ -51,6 +51,13 @@ class ODRDiagsEmitter {
bool diagnoseMismatch(const RecordDecl *FirstRecord,
const RecordDecl *SecondRecord) const;
+ /// Diagnose ODR mismatch between 2 ObjCInterfaceDecl.
+ ///
+ /// Returns true if found a mismatch and diagnosed it.
+ bool diagnoseMismatch(
+ const ObjCInterfaceDecl *FirstID, const ObjCInterfaceDecl *SecondID,
+ const struct ObjCInterfaceDecl::DefinitionData *SecondDD) const;
+
/// Diagnose ODR mismatch between 2 ObjCProtocolDecl.
///
/// Returns true if found a mismatch and diagnosed it.
@@ -97,6 +104,7 @@ class ODRDiagsEmitter {
Friend,
FunctionTemplate,
ObjCMethod,
+ ObjCIvar,
ObjCProperty,
Other
};
diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h
index a489bb73deb67..cedf644520fc3 100644
--- a/clang/include/clang/AST/ODRHash.h
+++ b/clang/include/clang/AST/ODRHash.h
@@ -59,6 +59,10 @@ class ODRHash {
// method compares more information than the AddDecl class.
void AddRecordDecl(const RecordDecl *Record);
+ // Use this for ODR checking ObjC interfaces. This
+ // method compares more information than the AddDecl class.
+ void AddObjCInterfaceDecl(const ObjCInterfaceDecl *Record);
+
// Use this for ODR checking functions between modules. This method compares
// more information than the AddDecl class. SkipBody will process the
// hash as if the function has no body.
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index d4303ef834c17..715249b9d6f5e 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -615,6 +615,22 @@ def note_module_odr_violation_definition_data : Note <
"%ordinal2 base class %3 with "
"%select{public|protected|private|no}4 access specifier}1">;
+def err_module_odr_violation_objc_interface : Error <
+ "%0 has
diff erent definitions in
diff erent modules; first
diff erence is "
+ "%select{definition in module '%2'|defined here}1 found "
+ "%select{"
+ "%select{no super class|super class with type %5}4|"
+ "instance variable '%4' access control is "
+ "%select{|@private|@protected|@public|@package}5"
+ "}3">;
+def note_module_odr_violation_objc_interface : Note <
+ "but in '%0' found "
+ "%select{"
+ "%select{no super class|super class with type %3}2|"
+ "instance variable '%2' access control is "
+ "%select{|@private|@protected|@public|@package}3"
+ "}1">;
+
def err_module_odr_violation_template_parameter : Error <
"%q0 has
diff erent definitions in
diff erent modules; first
diff erence is "
"%select{definition in module '%2'|defined here}1 found "
@@ -637,12 +653,14 @@ def err_module_odr_violation_mismatch_decl : Error<
"%select{definition in module '%2'|defined here}1 found "
"%select{end of class|public access specifier|private access specifier|"
"protected access specifier|static assert|field|method|type alias|typedef|"
- "data member|friend declaration|function template|method|property}3">;
+ "data member|friend declaration|function template|method|instance variable|"
+ "property}3">;
def note_module_odr_violation_mismatch_decl : Note<
"but in %select{'%1'|definition here}0 found "
"%select{end of class|public access specifier|private access specifier|"
"protected access specifier|static assert|field|method|type alias|typedef|"
- "data member|friend declaration|function template|method|property}2">;
+ "data member|friend declaration|function template|method|instance variable|"
+ "property}2">;
def err_module_odr_violation_record : Error<
"%q0 has
diff erent definitions in
diff erent modules; first
diff erence is "
@@ -937,14 +955,14 @@ def err_module_odr_violation_mismatch_decl_unknown : Error<
"%q0 %select{with definition in module '%2'|defined here}1 has
diff erent "
"definitions in
diff erent modules; first
diff erence is this "
"%select{||||static assert|field|method|type alias|typedef|data member|"
- "friend declaration|function template|method|"
+ "friend declaration|function template|method|instance variable|"
"property|unexpected decl}3">;
def note_module_odr_violation_mismatch_decl_unknown : Note<
"but in %select{'%1'|definition here}0 found "
"%select{||||
diff erent static assert|
diff erent field|
diff erent method|"
"
diff erent type alias|
diff erent typedef|
diff erent data member|"
"
diff erent friend declaration|
diff erent function template|
diff erent method|"
- "
diff erent property|another unexpected decl}2">;
+ "
diff erent instance variable|
diff erent property|another unexpected decl}2">;
def remark_sanitize_address_insert_extra_padding_accepted : Remark<
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index 5eb4834444ab6..5cdbdfe4e38d4 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -1149,6 +1149,9 @@ class ASTReader
using DataPointers =
std::pair<CXXRecordDecl *, struct CXXRecordDecl::DefinitionData *>;
+ using ObjCInterfaceDataPointers =
+ std::pair<ObjCInterfaceDecl *,
+ struct ObjCInterfaceDecl::DefinitionData *>;
using ObjCProtocolDataPointers =
std::pair<ObjCProtocolDecl *, struct ObjCProtocolDecl::DefinitionData *>;
@@ -1168,6 +1171,11 @@ class ASTReader
llvm::SmallDenseMap<EnumDecl *, llvm::SmallVector<EnumDecl *, 2>, 2>
PendingEnumOdrMergeFailures;
+ /// ObjCInterfaceDecl in which we found an ODR violation.
+ llvm::SmallDenseMap<ObjCInterfaceDecl *,
+ llvm::SmallVector<ObjCInterfaceDataPointers, 2>, 2>
+ PendingObjCInterfaceOdrMergeFailures;
+
/// ObjCProtocolDecl in which we found an ODR violation.
llvm::SmallDenseMap<ObjCProtocolDecl *,
llvm::SmallVector<ObjCProtocolDataPointers, 2>, 2>
diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp
index 6b21f7890b782..33b9d33075a2a 100644
--- a/clang/lib/AST/DeclObjC.cpp
+++ b/clang/lib/AST/DeclObjC.cpp
@@ -778,6 +778,33 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupPrivateMethod(
return Method;
}
+unsigned ObjCInterfaceDecl::getODRHash() {
+ assert(hasDefinition() && "ODRHash only for records with definitions");
+
+ // Previously calculated hash is stored in DefinitionData.
+ if (hasODRHash())
+ return data().ODRHash;
+
+ // Only calculate hash on first call of getODRHash per record.
+ ODRHash Hasher;
+ Hasher.AddObjCInterfaceDecl(getDefinition());
+ data().ODRHash = Hasher.CalculateHash();
+ setHasODRHash(true);
+
+ return data().ODRHash;
+}
+
+bool ObjCInterfaceDecl::hasODRHash() const {
+ if (!hasDefinition())
+ return false;
+ return data().HasODRHash;
+}
+
+void ObjCInterfaceDecl::setHasODRHash(bool HasHash) {
+ assert(hasDefinition() && "Cannot set ODRHash without definition");
+ data().HasODRHash = HasHash;
+}
+
//===----------------------------------------------------------------------===//
// ObjCMethodDecl
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/ODRDiagsEmitter.cpp b/clang/lib/AST/ODRDiagsEmitter.cpp
index b5888fc47a270..bd23fda6ac921 100644
--- a/clang/lib/AST/ODRDiagsEmitter.cpp
+++ b/clang/lib/AST/ODRDiagsEmitter.cpp
@@ -615,6 +615,8 @@ ODRDiagsEmitter::FindTypeDiffs(DeclHashes &FirstHashes,
return FunctionTemplate;
case Decl::ObjCMethod:
return ObjCMethod;
+ case Decl::ObjCIvar:
+ return ObjCIvar;
case Decl::ObjCProperty:
return ObjCProperty;
}
@@ -675,6 +677,8 @@ void ODRDiagsEmitter::diagnoseSubMismatchDifferentDeclKinds(
if (DiffType == EndOfClass) {
if (auto *Tag = dyn_cast<TagDecl>(Container))
Loc = Tag->getBraceRange().getEnd();
+ else if (auto *IF = dyn_cast<ObjCInterfaceDecl>(Container))
+ Loc = IF->getAtEndRange().getBegin();
else
Loc = Container->getEndLoc();
} else {
@@ -970,6 +974,7 @@ bool ODRDiagsEmitter::diagnoseMismatch(
case PrivateSpecifer:
case ProtectedSpecifer:
case ObjCMethod:
+ case ObjCIvar:
case ObjCProperty:
llvm_unreachable("Invalid
diff type");
@@ -1604,6 +1609,7 @@ bool ODRDiagsEmitter::diagnoseMismatch(const RecordDecl *FirstRecord,
case FunctionTemplate:
// Cannot be contained by RecordDecl, invalid in this context.
case ObjCMethod:
+ case ObjCIvar:
case ObjCProperty:
llvm_unreachable("Invalid
diff type");
@@ -1908,6 +1914,189 @@ bool ODRDiagsEmitter::diagnoseMismatch(const EnumDecl *FirstEnum,
return false;
}
+bool ODRDiagsEmitter::diagnoseMismatch(
+ const ObjCInterfaceDecl *FirstID, const ObjCInterfaceDecl *SecondID,
+ const struct ObjCInterfaceDecl::DefinitionData *SecondDD) const {
+ // Multiple
diff erent declarations got merged together; tell the user
+ // where they came from.
+ if (FirstID == SecondID)
+ return false;
+
+ std::string FirstModule = getOwningModuleNameForDiagnostic(FirstID);
+ std::string SecondModule = getOwningModuleNameForDiagnostic(SecondID);
+
+ // Keep in sync with err_module_odr_violation_objc_interface.
+ enum ODRInterfaceDifference {
+ SuperClassType,
+ IVarAccess,
+ };
+
+ auto DiagError = [FirstID, &FirstModule,
+ this](SourceLocation Loc, SourceRange Range,
+ ODRInterfaceDifference DiffType) {
+ return Diag(Loc, diag::err_module_odr_violation_objc_interface)
+ << FirstID << FirstModule.empty() << FirstModule << Range
+ << DiffType;
+ };
+ auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range,
+ ODRInterfaceDifference DiffType) {
+ return Diag(Loc, diag::note_module_odr_violation_objc_interface)
+ << SecondModule << Range << DiffType;
+ };
+
+ const struct ObjCInterfaceDecl::DefinitionData *FirstDD = &FirstID->data();
+ assert(FirstDD && SecondDD && "Definitions without DefinitionData");
+ if (FirstDD != SecondDD) {
+ // Check for matching super class.
+ auto GetSuperClassSourceRange = [](const TypeSourceInfo *SuperInfo,
+ const ObjCInterfaceDecl *ID) {
+ if (!SuperInfo)
+ return ID->getSourceRange();
+ TypeLoc Loc = SuperInfo->getTypeLoc();
+ return SourceRange(Loc.getBeginLoc(), Loc.getEndLoc());
+ };
+
+ ObjCInterfaceDecl *FirstSuperClass = FirstID->getSuperClass();
+ ObjCInterfaceDecl *SecondSuperClass = nullptr;
+ const TypeSourceInfo *FirstSuperInfo = FirstID->getSuperClassTInfo();
+ const TypeSourceInfo *SecondSuperInfo = SecondDD->SuperClassTInfo;
+ if (SecondSuperInfo)
+ SecondSuperClass =
+ SecondSuperInfo->getType()->castAs<ObjCObjectType>()->getInterface();
+
+ if ((FirstSuperClass && SecondSuperClass &&
+ FirstSuperClass->getODRHash() != SecondSuperClass->getODRHash()) ||
+ (FirstSuperClass && !SecondSuperClass) ||
+ (!FirstSuperClass && SecondSuperClass)) {
+ QualType FirstType;
+ if (FirstSuperInfo)
+ FirstType = FirstSuperInfo->getType();
+
+ DiagError(FirstID->getLocation(),
+ GetSuperClassSourceRange(FirstSuperInfo, FirstID),
+ SuperClassType)
+ << (bool)FirstSuperInfo << FirstType;
+
+ QualType SecondType;
+ if (SecondSuperInfo)
+ SecondType = SecondSuperInfo->getType();
+
+ DiagNote(SecondID->getLocation(),
+ GetSuperClassSourceRange(SecondSuperInfo, SecondID),
+ SuperClassType)
+ << (bool)SecondSuperInfo << SecondType;
+ return true;
+ }
+
+ // Check both interfaces reference the same protocols.
+ auto &FirstProtos = FirstID->getReferencedProtocols();
+ auto &SecondProtos = SecondDD->ReferencedProtocols;
+ if (diagnoseSubMismatchProtocols(FirstProtos, FirstID, FirstModule,
+ SecondProtos, SecondID, SecondModule))
+ return true;
+ }
+
+ auto PopulateHashes = [](DeclHashes &Hashes, const ObjCInterfaceDecl *ID,
+ const DeclContext *DC) {
+ for (auto *D : ID->decls()) {
+ if (!ODRHash::isSubDeclToBeProcessed(D, DC))
+ continue;
+ Hashes.emplace_back(D, computeODRHash(D));
+ }
+ };
+
+ DeclHashes FirstHashes;
+ DeclHashes SecondHashes;
+ PopulateHashes(FirstHashes, FirstID, FirstID);
+ PopulateHashes(SecondHashes, SecondID, FirstID);
+
+ DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes);
+ ODRMismatchDecl FirstDiffType = DR.FirstDiffType;
+ ODRMismatchDecl SecondDiffType = DR.SecondDiffType;
+ const Decl *FirstDecl = DR.FirstDecl;
+ const Decl *SecondDecl = DR.SecondDecl;
+
+ if (FirstDiffType == Other || SecondDiffType == Other) {
+ diagnoseSubMismatchUnexpected(DR, FirstID, FirstModule, SecondID,
+ SecondModule);
+ return true;
+ }
+
+ if (FirstDiffType != SecondDiffType) {
+ diagnoseSubMismatchDifferentDeclKinds(DR, FirstID, FirstModule, SecondID,
+ SecondModule);
+ return true;
+ }
+
+ assert(FirstDiffType == SecondDiffType);
+ switch (FirstDiffType) {
+ // Already handled.
+ case EndOfClass:
+ case Other:
+ // Cannot be contained by ObjCInterfaceDecl, invalid in this context.
+ case Field:
+ case TypeDef:
+ case Var:
+ // C++ only, invalid in this context.
+ case PublicSpecifer:
+ case PrivateSpecifer:
+ case ProtectedSpecifer:
+ case StaticAssert:
+ case CXXMethod:
+ case TypeAlias:
+ case Friend:
+ case FunctionTemplate:
+ llvm_unreachable("Invalid
diff type");
+
+ case ObjCMethod: {
+ if (diagnoseSubMismatchObjCMethod(FirstID, FirstModule, SecondModule,
+ cast<ObjCMethodDecl>(FirstDecl),
+ cast<ObjCMethodDecl>(SecondDecl)))
+ return true;
+ break;
+ }
+ case ObjCIvar: {
+ if (diagnoseSubMismatchField(FirstID, FirstModule, SecondModule,
+ cast<FieldDecl>(FirstDecl),
+ cast<FieldDecl>(SecondDecl)))
+ return true;
+
+ // Check if the access match.
+ const ObjCIvarDecl *FirstIvar = cast<ObjCIvarDecl>(FirstDecl);
+ const ObjCIvarDecl *SecondIvar = cast<ObjCIvarDecl>(SecondDecl);
+ if (FirstIvar->getCanonicalAccessControl() !=
+ SecondIvar->getCanonicalAccessControl()) {
+ DiagError(FirstIvar->getLocation(), FirstIvar->getSourceRange(),
+ IVarAccess)
+ << FirstIvar->getName()
+ << (int)FirstIvar->getCanonicalAccessControl();
+ DiagNote(SecondIvar->getLocation(), SecondIvar->getSourceRange(),
+ IVarAccess)
+ << SecondIvar->getName()
+ << (int)SecondIvar->getCanonicalAccessControl();
+ return true;
+ }
+ break;
+ }
+ case ObjCProperty: {
+ if (diagnoseSubMismatchObjCProperty(FirstID, FirstModule, SecondModule,
+ cast<ObjCPropertyDecl>(FirstDecl),
+ cast<ObjCPropertyDecl>(SecondDecl)))
+ return true;
+ break;
+ }
+ }
+
+ Diag(FirstDecl->getLocation(),
+ diag::err_module_odr_violation_mismatch_decl_unknown)
+ << FirstID << FirstModule.empty() << FirstModule << FirstDiffType
+ << FirstDecl->getSourceRange();
+ Diag(SecondDecl->getLocation(),
+ diag::note_module_odr_violation_mismatch_decl_unknown)
+ << SecondModule << FirstDiffType << SecondDecl->getSourceRange();
+ return true;
+}
+
bool ODRDiagsEmitter::diagnoseMismatch(
const ObjCProtocolDecl *FirstProtocol,
const ObjCProtocolDecl *SecondProtocol,
@@ -1975,6 +2164,7 @@ bool ODRDiagsEmitter::diagnoseMismatch(
case Field:
case TypeDef:
case Var:
+ case ObjCIvar:
// C++ only, invalid in this context.
case PublicSpecifer:
case PrivateSpecifer:
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 6912f67daa8f8..3374b49f5d8e2 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -343,6 +343,11 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
Inherited::VisitFieldDecl(D);
}
+ void VisitObjCIvarDecl(const ObjCIvarDecl *D) {
+ ID.AddInteger(D->getCanonicalAccessControl());
+ Inherited::VisitObjCIvarDecl(D);
+ }
+
void VisitObjCPropertyDecl(const ObjCPropertyDecl *D) {
ID.AddInteger(D->getPropertyAttributes());
ID.AddInteger(D->getPropertyImplementation());
@@ -537,6 +542,7 @@ bool ODRHash::isSubDeclToBeProcessed(const Decl *D, const DeclContext *Parent) {
case Decl::Typedef:
case Decl::Var:
case Decl::ObjCMethod:
+ case Decl::ObjCIvar:
case Decl::ObjCProperty:
return true;
}
@@ -613,6 +619,33 @@ void ODRHash::AddRecordDecl(const RecordDecl *Record) {
AddSubDecl(SubDecl);
}
+void ODRHash::AddObjCInterfaceDecl(const ObjCInterfaceDecl *IF) {
+ AddDecl(IF);
+
+ auto *SuperClass = IF->getSuperClass();
+ AddBoolean(SuperClass);
+ if (SuperClass)
+ ID.AddInteger(SuperClass->getODRHash());
+
+ // Hash referenced protocols.
+ ID.AddInteger(IF->getReferencedProtocols().size());
+ for (const ObjCProtocolDecl *RefP : IF->protocols()) {
+ // Hash the name only as a referenced protocol can be a forward declaration.
+ AddDeclarationName(RefP->getDeclName());
+ }
+
+ // Filter out sub-Decls which will not be processed in order to get an
+ // accurate count of Decl's.
+ llvm::SmallVector<const Decl *, 16> Decls;
+ for (Decl *SubDecl : IF->decls())
+ if (isSubDeclToBeProcessed(SubDecl, IF))
+ Decls.push_back(SubDecl);
+
+ ID.AddInteger(Decls.size());
+ for (auto *SubDecl : Decls)
+ AddSubDecl(SubDecl);
+}
+
void ODRHash::AddFunctionDecl(const FunctionDecl *Function,
bool SkipBody) {
assert(Function && "Expecting non-null pointer.");
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 753d4166f5306..72a4be8ab6af3 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -6857,6 +6857,7 @@ void TypeLocReader::VisitPackExpansionTypeLoc(PackExpansionTypeLoc TL) {
void TypeLocReader::VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) {
TL.setNameLoc(readSourceLocation());
+ TL.setNameEndLoc(readSourceLocation());
}
void TypeLocReader::VisitObjCTypeParamTypeLoc(ObjCTypeParamTypeLoc TL) {
@@ -9507,6 +9508,7 @@ void ASTReader::diagnoseOdrViolations() {
PendingRecordOdrMergeFailures.empty() &&
PendingFunctionOdrMergeFailures.empty() &&
PendingEnumOdrMergeFailures.empty() &&
+ PendingObjCInterfaceOdrMergeFailures.empty() &&
PendingObjCProtocolOdrMergeFailures.empty())
return;
@@ -9538,6 +9540,16 @@ void ASTReader::diagnoseOdrViolations() {
D->decls_begin();
}
+ // Trigger the import of the full interface definition.
+ auto ObjCInterfaceOdrMergeFailures =
+ std::move(PendingObjCInterfaceOdrMergeFailures);
+ PendingObjCInterfaceOdrMergeFailures.clear();
+ for (auto &Merge : ObjCInterfaceOdrMergeFailures) {
+ Merge.first->decls_begin();
+ for (auto &InterfacePair : Merge.second)
+ InterfacePair.first->decls_begin();
+ }
+
// Trigger the import of functions.
auto FunctionOdrMergeFailures = std::move(PendingFunctionOdrMergeFailures);
PendingFunctionOdrMergeFailures.clear();
@@ -9657,6 +9669,7 @@ void ASTReader::diagnoseOdrViolations() {
if (OdrMergeFailures.empty() && RecordOdrMergeFailures.empty() &&
FunctionOdrMergeFailures.empty() && EnumOdrMergeFailures.empty() &&
+ ObjCInterfaceOdrMergeFailures.empty() &&
ObjCProtocolOdrMergeFailures.empty())
return;
@@ -9749,6 +9762,25 @@ void ASTReader::diagnoseOdrViolations() {
assert(Diagnosed && "Unable to emit ODR diagnostic.");
}
+ for (auto &Merge : ObjCInterfaceOdrMergeFailures) {
+ // If we've already pointed out a specific problem with this interface,
+ // don't bother issuing a general "something's
diff erent" diagnostic.
+ if (!DiagnosedOdrMergeFailures.insert(Merge.first).second)
+ continue;
+
+ bool Diagnosed = false;
+ ObjCInterfaceDecl *FirstID = Merge.first;
+ for (auto &InterfacePair : Merge.second) {
+ if (DiagsEmitter.diagnoseMismatch(FirstID, InterfacePair.first,
+ InterfacePair.second)) {
+ Diagnosed = true;
+ break;
+ }
+ }
+ (void)Diagnosed;
+ assert(Diagnosed && "Unable to emit ODR diagnostic.");
+ }
+
for (auto &Merge : ObjCProtocolOdrMergeFailures) {
// If we've already pointed out a specific problem with this protocol,
// don't bother issuing a general "something's
diff erent" diagnostic.
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 6c7198d7935a1..8cb513eff13e0 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1207,6 +1207,8 @@ void ASTDeclReader::ReadObjCDefinitionData(
Data.EndLoc = readSourceLocation();
Data.HasDesignatedInitializers = Record.readInt();
+ Data.ODRHash = Record.readInt();
+ Data.HasODRHash = true;
// Read the directly referenced protocols and their SourceLocations.
unsigned NumProtocols = Record.readInt();
@@ -1234,13 +1236,16 @@ void ASTDeclReader::ReadObjCDefinitionData(
void ASTDeclReader::MergeDefinitionData(ObjCInterfaceDecl *D,
struct ObjCInterfaceDecl::DefinitionData &&NewDD) {
struct ObjCInterfaceDecl::DefinitionData &DD = D->data();
- if (DD.Definition != NewDD.Definition) {
- Reader.MergedDeclContexts.insert(
- std::make_pair(NewDD.Definition, DD.Definition));
- Reader.mergeDefinitionVisibility(DD.Definition, NewDD.Definition);
- }
+ if (DD.Definition == NewDD.Definition)
+ return;
- // FIXME: odr checking?
+ Reader.MergedDeclContexts.insert(
+ std::make_pair(NewDD.Definition, DD.Definition));
+ Reader.mergeDefinitionVisibility(DD.Definition, NewDD.Definition);
+
+ if (D->getODRHash() != NewDD.ODRHash)
+ Reader.PendingObjCInterfaceOdrMergeFailures[DD.Definition].push_back(
+ {NewDD.Definition, &NewDD});
}
void ASTDeclReader::VisitObjCInterfaceDecl(ObjCInterfaceDecl *ID) {
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 43a74cdb5a5b7..7fd52c267592f 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -569,6 +569,7 @@ void TypeLocWriter::VisitPackExpansionTypeLoc(PackExpansionTypeLoc TL) {
void TypeLocWriter::VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) {
addSourceLocation(TL.getNameLoc());
+ addSourceLocation(TL.getNameEndLoc());
}
void TypeLocWriter::VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) {
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index f7daa5076d2bb..a58e0d796b31e 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -775,6 +775,7 @@ void ASTDeclWriter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) {
Record.AddTypeSourceInfo(D->getSuperClassTInfo());
Record.AddSourceLocation(D->getEndOfDefinitionLoc());
Record.push_back(Data.HasDesignatedInitializers);
+ Record.push_back(D->getODRHash());
// Write out the protocols that are directly referenced by the @interface.
Record.push_back(Data.ReferencedProtocols.size());
diff --git a/clang/test/Modules/compare-objc-interface.m b/clang/test/Modules/compare-objc-interface.m
new file mode 100644
index 0000000000000..71f1cc853a7a3
--- /dev/null
+++ b/clang/test/Modules/compare-objc-interface.m
@@ -0,0 +1,424 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// Build first header file
+// RUN: echo "#define FIRST" >> %t/include/first.h
+// RUN: cat %t/test.m >> %t/include/first.h
+// RUN: echo "#undef FIRST" >> %t/include/first.h
+
+// Build second header file
+// RUN: echo "#define SECOND" >> %t/include/second.h
+// RUN: cat %t/test.m >> %t/include/second.h
+// RUN: echo "#undef SECOND" >> %t/include/second.h
+
+// Test that each header can compile
+// RUN: %clang_cc1 -fsyntax-only -x objective-c %t/include/first.h -fblocks -fobjc-arc
+// RUN: %clang_cc1 -fsyntax-only -x objective-c %t/include/second.h -fblocks -fobjc-arc
+
+// Run test
+// RUN: %clang_cc1 -I%t/include -verify %t/test.m -fblocks -fobjc-arc \
+// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache
+
+// Test that we don't accept
diff erent class definitions with the same name
+// from multiple modules but detect mismatches and provide actionable
+// diagnostic.
+
+//--- include/common.h
+#ifndef COMMON_H
+#define COMMON_H
+ at interface NSObject @end
+ at protocol CommonProtocol @end
+ at protocol ExtraProtocol @end
+#endif
+
+//--- include/first-empty.h
+//--- include/module.modulemap
+module First {
+ module Empty {
+ header "first-empty.h"
+ }
+ module Hidden {
+ header "first.h"
+ export *
+ }
+}
+module Second {
+ header "second.h"
+ export *
+}
+
+//--- test.m
+#if defined(FIRST) || defined(SECOND)
+# include "common.h"
+#endif
+
+#if !defined(FIRST) && !defined(SECOND)
+# include "first-empty.h"
+# include "second.h"
+#endif
+
+#if defined(FIRST)
+ at class CompareForwardDeclaration1;
+ at interface CompareForwardDeclaration2: NSObject @end
+#elif defined(SECOND)
+ at interface CompareForwardDeclaration1: NSObject @end
+ at class CompareForwardDeclaration2;
+#else
+CompareForwardDeclaration1 *compareForwardDeclaration1;
+CompareForwardDeclaration2 *compareForwardDeclaration2;
+#endif
+
+#if defined(FIRST)
+ at interface CompareMatchingSuperclass: NSObject @end
+
+ at interface CompareSuperclassPresence1: NSObject @end
+ at interface CompareSuperclassPresence2 @end
+
+ at interface CompareDifferentSuperclass: NSObject @end
+#elif defined(SECOND)
+ at interface CompareMatchingSuperclass: NSObject @end
+
+ at interface CompareSuperclassPresence1 @end
+ at interface CompareSuperclassPresence2: NSObject @end
+
+ at interface DifferentSuperclass: NSObject @end
+ at interface CompareDifferentSuperclass: DifferentSuperclass @end
+#else
+CompareMatchingSuperclass *compareMatchingSuperclass;
+CompareSuperclassPresence1 *compareSuperclassPresence1;
+// expected-error at first.h:* {{'CompareSuperclassPresence1' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found super class with type 'NSObject'}}
+// expected-note at second.h:* {{but in 'Second' found no super class}}
+CompareSuperclassPresence2 *compareSuperclassPresence2;
+// expected-error at first.h:* {{'CompareSuperclassPresence2' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found no super class}}
+// expected-note at second.h:* {{but in 'Second' found super class with type 'NSObject'}}
+CompareDifferentSuperclass *compareDifferentSuperclass;
+// expected-error at first.h:* {{'CompareDifferentSuperclass' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found super class with type 'NSObject'}}
+// expected-note at second.h:* {{but in 'Second' found super class with type 'DifferentSuperclass'}}
+#endif
+
+#if defined(FIRST)
+ at interface CompareMatchingConformingProtocols: NSObject<CommonProtocol> @end
+ at protocol ForwardProtocol;
+ at interface CompareMatchingConformingForwardProtocols: NSObject<ForwardProtocol> @end
+
+ at interface CompareProtocolPresence1: NSObject<CommonProtocol> @end
+ at interface CompareProtocolPresence2: NSObject @end
+
+ at interface CompareDifferentProtocols: NSObject<CommonProtocol> @end
+ at interface CompareProtocolOrder: NSObject<CommonProtocol, ExtraProtocol> @end
+#elif defined(SECOND)
+ at interface CompareMatchingConformingProtocols: NSObject<CommonProtocol> @end
+ at protocol ForwardProtocol @end
+ at interface CompareMatchingConformingForwardProtocols: NSObject<ForwardProtocol> @end
+
+ at interface CompareProtocolPresence1: NSObject @end
+ at interface CompareProtocolPresence2: NSObject<CommonProtocol> @end
+
+ at interface CompareDifferentProtocols: NSObject<ExtraProtocol> @end
+ at interface CompareProtocolOrder: NSObject<ExtraProtocol, CommonProtocol> @end
+#else
+CompareMatchingConformingProtocols *compareMatchingConformingProtocols;
+CompareMatchingConformingForwardProtocols *compareMatchingConformingForwardProtocols;
+
+CompareProtocolPresence1 *compareProtocolPresence1;
+// expected-error at first.h:* {{'CompareProtocolPresence1' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found 1 referenced protocol}}
+// expected-note at second.h:* {{but in 'Second' found 0 referenced protocols}}
+CompareProtocolPresence2 *compareProtocolPresence2;
+// expected-error at first.h:* {{'CompareProtocolPresence2' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found 0 referenced protocols}}
+// expected-note at second.h:* {{but in 'Second' found 1 referenced protocol}}
+
+CompareDifferentProtocols *compareDifferentProtocols;
+// expected-error at first.h:* {{'CompareDifferentProtocols' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found 1st referenced protocol with name 'CommonProtocol'}}
+// expected-note at second.h:* {{but in 'Second' found 1st referenced protocol with
diff erent name 'ExtraProtocol'}}
+CompareProtocolOrder *compareProtocolOrder;
+// expected-error at first.h:* {{'CompareProtocolOrder' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found 1st referenced protocol with name 'CommonProtocol'}}
+// expected-note at second.h:* {{but in 'Second' found 1st referenced protocol with
diff erent name 'ExtraProtocol'}}
+#endif
+
+#if defined(FIRST)
+ at interface CompareMatchingIVars: NSObject { int ivarName; } @end
+
+ at interface CompareIVarPresence1: NSObject @end
+ at interface CompareIVarPresence2: NSObject { int ivarPresence2; } @end
+
+ at interface CompareIVarName: NSObject { int ivarName; } @end
+ at interface CompareIVarType: NSObject { int ivarType; } @end
+ at interface CompareIVarOrder: NSObject {
+ int ivarNameInt;
+ float ivarNameFloat;
+}
+ at end
+
+ at interface CompareIVarVisibilityExplicit: NSObject {
+ at public
+ int ivarVisibility;
+}
+ at end
+ at interface CompareIVarVisibilityDefault: NSObject {
+ int ivarVisibilityDefault;
+}
+ at end
+#elif defined(SECOND)
+ at interface CompareMatchingIVars: NSObject { int ivarName; } @end
+
+ at interface CompareIVarPresence1: NSObject { int ivarPresence1; } @end
+ at interface CompareIVarPresence2: NSObject @end
+
+ at interface CompareIVarName: NSObject { int
diff erentIvarName; } @end
+ at interface CompareIVarType: NSObject { float ivarType; } @end
+ at interface CompareIVarOrder: NSObject {
+ float ivarNameFloat;
+ int ivarNameInt;
+}
+ at end
+
+ at interface CompareIVarVisibilityExplicit: NSObject {
+ at private
+ int ivarVisibility;
+}
+ at end
+ at interface CompareIVarVisibilityDefault: NSObject {
+ at public
+ int ivarVisibilityDefault;
+}
+ at end
+#else
+CompareMatchingIVars *compareMatchingIVars;
+
+CompareIVarPresence1 *compareIVarPresence1;
+// expected-error at second.h:* {{'CompareIVarPresence1::ivarPresence1' from module 'Second' is not present in definition of 'CompareIVarPresence1' in module 'First.Hidden'}}
+// expected-note at first.h:* {{definition has no member 'ivarPresence1'}}
+CompareIVarPresence2 *compareIVarPresence2;
+// expected-error at first.h:* {{'CompareIVarPresence2' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found instance variable}}
+// expected-note at second.h:* {{but in 'Second' found end of class}}
+
+CompareIVarName *compareIVarName;
+// expected-error at second.h:* {{'CompareIVarName::
diff erentIvarName' from module 'Second' is not present in definition of 'CompareIVarName' in module 'First.Hidden'}}
+// expected-note at first.h:* {{definition has no member '
diff erentIvarName'}}
+CompareIVarType *compareIVarType;
+// expected-error at second.h:* {{'CompareIVarType::ivarType' from module 'Second' is not present in definition of 'CompareIVarType' in module 'First.Hidden'}}
+// expected-note at first.h:* {{declaration of 'ivarType' does not match}}
+CompareIVarOrder *compareIVarOrder;
+// expected-error at first.h:* {{'CompareIVarOrder' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found field 'ivarNameInt'}}
+// expected-note at second.h:* {{but in 'Second' found field 'ivarNameFloat'}}
+
+CompareIVarVisibilityExplicit *compareIVarVisibilityExplicit;
+// expected-error at first.h:* {{'CompareIVarVisibilityExplicit' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found instance variable 'ivarVisibility' access control is @public}}
+// expected-note at second.h:* {{but in 'Second' found instance variable 'ivarVisibility' access control is @private}}
+CompareIVarVisibilityDefault *compareIVarVisibilityDefault;
+// expected-error at first.h:* {{'CompareIVarVisibilityDefault' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found instance variable 'ivarVisibilityDefault' access control is @protected}}
+// expected-note at second.h:* {{but in 'Second' found instance variable 'ivarVisibilityDefault' access control is @public}}
+#endif
+
+#if defined(FIRST)
+ at interface CompareMatchingMethods: NSObject
+- (float)matchingMethod:(int)arg;
+ at end
+
+ at interface CompareMethodPresence1: NSObject
+- (void)presenceMethod1;
+ at end
+ at interface CompareMethodPresence2: NSObject
+ at end
+
+ at interface CompareMethodName: NSObject
+- (void)methodNameA;
+ at end
+
+ at interface CompareMethodArgCount: NSObject
+- (void)methodArgCount:(int)arg0 :(int)arg1;
+ at end
+ at interface CompareMethodArgName: NSObject
+- (void)methodArgName:(int)argNameA;
+ at end
+ at interface CompareMethodArgType: NSObject
+- (void)methodArgType:(int)argType;
+ at end
+
+ at interface CompareMethodReturnType: NSObject
+- (int)methodReturnType;
+ at end
+
+ at interface CompareMethodOrder: NSObject
+- (void)methodOrderFirst;
+- (void)methodOrderSecond;
+ at end
+
+ at interface CompareMethodClassInstance: NSObject
++ (void)methodClassInstance;
+ at end
+#elif defined(SECOND)
+ at interface CompareMatchingMethods: NSObject
+- (float)matchingMethod:(int)arg;
+ at end
+
+ at interface CompareMethodPresence1: NSObject
+ at end
+ at interface CompareMethodPresence2: NSObject
+- (void)presenceMethod2;
+ at end
+
+ at interface CompareMethodName: NSObject
+- (void)methodNameB;
+ at end
+
+ at interface CompareMethodArgCount: NSObject
+- (void)methodArgCount:(int)arg0;
+ at end
+ at interface CompareMethodArgName: NSObject
+- (void)methodArgName:(int)argNameB;
+ at end
+ at interface CompareMethodArgType: NSObject
+- (void)methodArgType:(float)argType;
+ at end
+
+ at interface CompareMethodReturnType: NSObject
+- (float)methodReturnType;
+ at end
+
+ at interface CompareMethodOrder: NSObject
+- (void)methodOrderSecond;
+- (void)methodOrderFirst;
+ at end
+
+ at interface CompareMethodClassInstance: NSObject
+- (void)methodClassInstance;
+ at end
+#else
+CompareMatchingMethods *compareMatchingMethods;
+CompareMethodPresence1 *compareMethodPresence1;
+// expected-error at first.h:* {{'CompareMethodPresence1' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found method}}
+// expected-note at second.h:* {{but in 'Second' found end of class}}
+CompareMethodPresence2 *compareMethodPresence2;
+// expected-error at first.h:* {{'CompareMethodPresence2' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found end of class}}
+// expected-note at second.h:* {{but in 'Second' found method}}
+CompareMethodName *compareMethodName;
+// expected-error at first.h:* {{'CompareMethodName' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found method 'methodNameA'}}
+// expected-note at second.h:* {{but in 'Second' found
diff erent method 'methodNameB'}}
+
+CompareMethodArgCount *compareMethodArgCount;
+// expected-error at first.h:* {{'CompareMethodArgCount' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found method 'methodArgCount::' that has 2 parameters}}
+// expected-note at second.h:* {{but in 'Second' found method 'methodArgCount:' that has 1 parameter}}
+CompareMethodArgName *compareMethodArgName;
+// expected-error at first.h:* {{'CompareMethodArgName' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found method 'methodArgName:' with 1st parameter named 'argNameA'}}
+// expected-note at second.h:* {{but in 'Second' found method 'methodArgName:' with 1st parameter named 'argNameB'}}
+CompareMethodArgType *compareMethodArgType;
+// expected-error at first.h:* {{'CompareMethodArgType' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found method 'methodArgType:' with 1st parameter of type 'int'}}
+// expected-note at second.h:* {{but in 'Second' found method 'methodArgType:' with 1st parameter of type 'float'}}
+
+CompareMethodReturnType *compareMethodReturnType;
+// expected-error at first.h:* {{'CompareMethodReturnType' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found method 'methodReturnType' with return type 'int'}}
+// expected-note at second.h:* {{but in 'Second' found method 'methodReturnType' with
diff erent return type 'float'}}
+
+CompareMethodOrder *compareMethodOrder;
+// expected-error at first.h:* {{'CompareMethodOrder' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found method 'methodOrderFirst'}}
+// expected-note at second.h:* {{but in 'Second' found
diff erent method 'methodOrderSecond'}}
+CompareMethodClassInstance *compareMethodClassInstance;
+// expected-error at first.h:* {{'CompareMethodClassInstance' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found class method 'methodClassInstance'}}
+// expected-note at second.h:* {{but in 'Second' found method 'methodClassInstance' as instance method}}
+#endif
+
+#if defined(FIRST)
+ at interface CompareMatchingProperties: NSObject
+ at property int matchingPropName;
+ at end
+
+ at interface ComparePropertyPresence1: NSObject
+ at property int propPresence1;
+ at end
+ at interface ComparePropertyPresence2: NSObject
+ at end
+
+ at interface ComparePropertyName: NSObject
+ at property int propNameA;
+ at end
+
+ at interface ComparePropertyType: NSObject
+ at property int propType;
+ at end
+
+ at interface ComparePropertyOrder: NSObject
+ at property int propOrderX;
+ at property int propOrderY;
+ at end
+
+ at interface CompareMatchingPropertyAttributes: NSObject
+ at property (nonatomic, assign) int matchingProp;
+ at end
+ at interface ComparePropertyAttributes: NSObject
+ at property (readonly) int propAttributes;
+ at end
+// Edge cases.
+ at interface CompareFirstImplAttribute: NSObject
+ at property int firstImplAttribute;
+ at end
+ at interface CompareLastImplAttribute: NSObject
+ at property (direct) int lastImplAttribute;
+ at end
+#elif defined(SECOND)
+ at interface CompareMatchingProperties: NSObject
+ at property int matchingPropName;
+ at end
+
+ at interface ComparePropertyPresence1: NSObject
+ at end
+ at interface ComparePropertyPresence2: NSObject
+ at property int propPresence2;
+ at end
+
+ at interface ComparePropertyName: NSObject
+ at property int propNameB;
+ at end
+
+ at interface ComparePropertyType: NSObject
+ at property float propType;
+ at end
+
+ at interface ComparePropertyOrder: NSObject
+ at property int propOrderY;
+ at property int propOrderX;
+ at end
+
+ at interface CompareMatchingPropertyAttributes: NSObject
+ at property (assign, nonatomic) int matchingProp;
+ at end
+ at interface ComparePropertyAttributes: NSObject
+ at property (readwrite) int propAttributes;
+ at end
+// Edge cases.
+ at interface CompareFirstImplAttribute: NSObject
+ at property (readonly) int firstImplAttribute;
+ at end
+ at interface CompareLastImplAttribute: NSObject
+ at property int lastImplAttribute;
+ at end
+#else
+CompareMatchingProperties *compareMatchingProperties;
+ComparePropertyPresence1 *comparePropertyPresence1;
+// expected-error at first.h:* {{'ComparePropertyPresence1' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found property}}
+// expected-note at second.h:* {{but in 'Second' found end of class}}
+ComparePropertyPresence2 *comparePropertyPresence2;
+// expected-error at first.h:* {{'ComparePropertyPresence2' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found end of class}}
+// expected-note at second.h:* {{but in 'Second' found property}}
+
+ComparePropertyName *comparePropertyName;
+// expected-error at first.h:* {{'ComparePropertyName' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found property 'propNameA'}}
+// expected-note at second.h:* {{but in 'Second' found property 'propNameB'}}
+ComparePropertyType *comparePropertyType;
+// expected-error at first.h:* {{'ComparePropertyType' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found property 'propType' with type 'int'}}
+// expected-note at second.h:* {{but in 'Second' found property 'propType' with type 'float'}}
+ComparePropertyOrder *comparePropertyOrder;
+// expected-error at first.h:* {{'ComparePropertyOrder' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found property 'propOrderX'}}
+// expected-note at second.h:* {{but in 'Second' found property 'propOrderY'}}
+
+CompareMatchingPropertyAttributes *compareMatchingPropertyAttributes;
+ComparePropertyAttributes *comparePropertyAttributes;
+// expected-error at first.h:* {{'ComparePropertyAttributes' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found property 'propAttributes' with 'readonly' attribute}}
+// expected-note at second.h:* {{but in 'Second' found property 'propAttributes' with
diff erent 'readonly' attribute}}
+CompareFirstImplAttribute *compareFirstImplAttribute;
+// expected-error at first.h:* {{'CompareFirstImplAttribute' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found property 'firstImplAttribute' with default 'readonly' attribute}}
+// expected-note at second.h:* {{but in 'Second' found property 'firstImplAttribute' with
diff erent 'readonly' attribute}}
+CompareLastImplAttribute *compareLastImplAttribute;
+// expected-error at first.h:* {{'CompareLastImplAttribute' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found property 'lastImplAttribute' with 'direct' attribute}}
+// expected-note at second.h:* {{but in 'Second' found property 'lastImplAttribute' with
diff erent 'direct' attribute}}
+#endif
diff --git a/clang/test/Modules/interface-diagnose-missing-import.m b/clang/test/Modules/interface-diagnose-missing-import.m
index a6b8c1b03a9dd..2b81983691f05 100644
--- a/clang/test/Modules/interface-diagnose-missing-import.m
+++ b/clang/test/Modules/interface-diagnose-missing-import.m
@@ -1,7 +1,8 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 %s -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -F%S/Inputs/interface-diagnose-missing-import -verify
// expected-no-diagnostics
- at interface Buggy
+ at interface NSObject @end
+ at interface Buggy : NSObject
@end
@import Foo.Bar;
diff --git a/clang/test/Modules/method_pool.m b/clang/test/Modules/method_pool.m
index 1d76a8404c6b1..7410e25975a0d 100644
--- a/clang/test/Modules/method_pool.m
+++ b/clang/test/Modules/method_pool.m
@@ -28,6 +28,8 @@ void testMethod5(id object, D* d) {
}
@import MethodPoolB;
+// expected-error at MethodPoolB.h:* {{'B' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'MethodPoolB' found no super class}}
+// expected-note at MethodPoolA.h:* {{but in 'MethodPoolA' found super class with type 'A'}}
void testMethod1Again(id object) {
[object method1];
More information about the cfe-commits
mailing list