]> granicus.if.org Git - clang/commitdiff
AST import of Objective-C categories.
authorDouglas Gregor <dgregor@apple.com>
Thu, 18 Feb 2010 01:47:50 +0000 (01:47 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 18 Feb 2010 01:47:50 +0000 (01:47 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@96551 91177308-0d34-0410-b5e6-96231b3b80d8

lib/AST/ASTImporter.cpp
test/ASTMerge/Inputs/category1.m [new file with mode: 0644]
test/ASTMerge/Inputs/category2.m [new file with mode: 0644]
test/ASTMerge/category.m [new file with mode: 0644]

index cfcf460e930d0fc4a73ea4f88109fbd042b4cbaf..cde3402f7aa7995895198e9116b0f8f1cc6cd5b4 100644 (file)
@@ -95,6 +95,7 @@ namespace {
     Decl *VisitImplicitParamDecl(ImplicitParamDecl *D);
     Decl *VisitParmVarDecl(ParmVarDecl *D);
     Decl *VisitObjCMethodDecl(ObjCMethodDecl *D);
+    Decl *VisitObjCCategoryDecl(ObjCCategoryDecl *D);
     Decl *VisitObjCProtocolDecl(ObjCProtocolDecl *D);
     Decl *VisitObjCInterfaceDecl(ObjCInterfaceDecl *D);
     Decl *VisitObjCPropertyDecl(ObjCPropertyDecl *D);
@@ -2159,8 +2160,84 @@ Decl *ASTNodeImporter::VisitObjCMethodDecl(ObjCMethodDecl *D) {
   return ToMethod;
 }
 
+Decl *ASTNodeImporter::VisitObjCCategoryDecl(ObjCCategoryDecl *D) {
+  // Import the major distinguishing characteristics of a category.
+  DeclContext *DC, *LexicalDC;
+  DeclarationName Name;
+  SourceLocation Loc;
+  if (ImportDeclParts(D, DC, LexicalDC, Name, Loc))
+    return 0;
+  
+  ObjCInterfaceDecl *ToInterface
+    = cast_or_null<ObjCInterfaceDecl>(Importer.Import(D->getClassInterface()));
+  if (!ToInterface)
+    return 0;
+  
+  // Determine if we've already encountered this category.
+  ObjCCategoryDecl *MergeWithCategory
+    = ToInterface->FindCategoryDeclaration(Name.getAsIdentifierInfo());
+  ObjCCategoryDecl *ToCategory = MergeWithCategory;
+  if (!ToCategory) {
+    ToCategory = ObjCCategoryDecl::Create(Importer.getToContext(), DC,
+                                          Importer.Import(D->getAtLoc()),
+                                          Loc, 
+                                       Importer.Import(D->getCategoryNameLoc()), 
+                                          Name.getAsIdentifierInfo());
+    ToCategory->setLexicalDeclContext(LexicalDC);
+    LexicalDC->addDecl(ToCategory);
+    Importer.Imported(D, ToCategory);
+    
+    // Link this category into its class's category list.
+    ToCategory->setClassInterface(ToInterface);
+    ToCategory->insertNextClassCategory();
+    
+    // Import protocols
+    llvm::SmallVector<ObjCProtocolDecl *, 4> Protocols;
+    llvm::SmallVector<SourceLocation, 4> ProtocolLocs;
+    ObjCCategoryDecl::protocol_loc_iterator FromProtoLoc
+      = D->protocol_loc_begin();
+    for (ObjCCategoryDecl::protocol_iterator FromProto = D->protocol_begin(),
+                                          FromProtoEnd = D->protocol_end();
+         FromProto != FromProtoEnd;
+         ++FromProto, ++FromProtoLoc) {
+      ObjCProtocolDecl *ToProto
+        = cast_or_null<ObjCProtocolDecl>(Importer.Import(*FromProto));
+      if (!ToProto)
+        return 0;
+      Protocols.push_back(ToProto);
+      ProtocolLocs.push_back(Importer.Import(*FromProtoLoc));
+    }
+    
+    // FIXME: If we're merging, make sure that the protocol list is the same.
+    ToCategory->setProtocolList(Protocols.data(), Protocols.size(),
+                                ProtocolLocs.data(), Importer.getToContext());
+    
+  } else {
+    Importer.Imported(D, ToCategory);
+  }
+  
+  // Import all of the members of this category.
+  for (DeclContext::decl_iterator FromMem = D->decls_begin(), 
+                              FromMemEnd = D->decls_end();
+       FromMem != FromMemEnd;
+       ++FromMem)
+    Importer.Import(*FromMem);
+  // If we have an implementation, import it as well.
+  if (D->getImplementation()) {
+    ObjCCategoryImplDecl *Impl
+      = cast<ObjCCategoryImplDecl>(Importer.Import(D->getImplementation()));
+    if (!Impl)
+      return 0;
+    
+    ToCategory->setImplementation(Impl);
+  }
+  
+  return ToCategory;
+}
+
 Decl *ASTNodeImporter::VisitObjCProtocolDecl(ObjCProtocolDecl *D) {
-  // Import the major distinguishing characteristics of an @protocol.
+  // Import the major distinguishing characteristics of a protocol.
   DeclContext *DC, *LexicalDC;
   DeclarationName Name;
   SourceLocation Loc;
@@ -2213,7 +2290,7 @@ Decl *ASTNodeImporter::VisitObjCProtocolDecl(ObjCProtocolDecl *D) {
     Importer.Imported(D, ToProto);
   }
 
-  // Import all of the members of this class.
+  // Import all of the members of this protocol.
   for (DeclContext::decl_iterator FromMem = D->decls_begin(), 
                                FromMemEnd = D->decls_end();
        FromMem != FromMemEnd;
@@ -2288,8 +2365,6 @@ Decl *ASTNodeImporter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) {
     ToIface->setProtocolList(Protocols.data(), Protocols.size(),
                              ProtocolLocs.data(), Importer.getToContext());
     
-    // FIXME: Import categories
-    
     // Import @end range
     ToIface->setAtEndRange(Importer.Import(D->getAtEndRange()));
   } else {
@@ -2323,6 +2398,12 @@ Decl *ASTNodeImporter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) {
     }
   }
   
+  // Import categories. When the categories themselves are imported, they'll
+  // hook themselves into this interface.
+  for (ObjCCategoryDecl *FromCat = D->getCategoryList(); FromCat;
+       FromCat = FromCat->getNextClassCategory())
+    Importer.Import(FromCat);
+  
   // Import all of the members of this class.
   for (DeclContext::decl_iterator FromMem = D->decls_begin(), 
                                FromMemEnd = D->decls_end();
diff --git a/test/ASTMerge/Inputs/category1.m b/test/ASTMerge/Inputs/category1.m
new file mode 100644 (file)
index 0000000..ade1c6c
--- /dev/null
@@ -0,0 +1,25 @@
+@interface I1 
+@end
+
+// Matching category
+@interface I1 (Cat1)
+- (int)method0;
+@end
+
+// Matching class extension
+@interface I1 ()
+- (int)method1;
+@end
+
+// Mismatched category
+@interface I1 (Cat2)
+- (int)method2;
+@end
+
+@interface I2
+@end
+
+// Mismatched class extension
+@interface I2 ()
+- (int)method3;
+@end
diff --git a/test/ASTMerge/Inputs/category2.m b/test/ASTMerge/Inputs/category2.m
new file mode 100644 (file)
index 0000000..f66c208
--- /dev/null
@@ -0,0 +1,27 @@
+typedef int Int;
+
+@interface I1 
+@end
+
+// Matching category
+@interface I1 (Cat1)
+- (Int)method0;
+@end
+
+// Matching class extension
+@interface I1 ()
+- (Int)method1;
+@end
+
+// Mismatched category
+@interface I1 (Cat2)
+- (float)method2;
+@end
+
+@interface I2
+@end
+
+// Mismatched class extension
+@interface I2 ()
+- (float)method3;
+@end
diff --git a/test/ASTMerge/category.m b/test/ASTMerge/category.m
new file mode 100644 (file)
index 0000000..bf0d11b
--- /dev/null
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -emit-pch -o %t.1.ast %S/Inputs/category1.m
+// RUN: %clang_cc1 -emit-pch -o %t.2.ast %S/Inputs/category2.m
+// RUN: %clang_cc1 -ast-merge %t.1.ast -ast-merge %t.2.ast -fsyntax-only %s 2>&1 | FileCheck %s
+
+// CHECK: category2.m:18:1: error: instance method 'method2' has incompatible result types in different translation units ('float' vs. 'int')
+// CHECK: category1.m:16:1: note: instance method 'method2' also declared here
+// CHECK: category2.m:26:1: error: instance method 'method3' has incompatible result types in different translation units ('float' vs. 'int')
+// CHECK: category1.m:24:1: note: instance method 'method3' also declared here
+// CHECK: 4 diagnostics generated.