modern objective-c translator. named aggregate types
defined inside the objc class belong to class's
decl. scope. This is to conform to objective-c
rules. // rdar://11351299


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@155855 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Rewrite/RewriteModernObjC.cpp b/lib/Rewrite/RewriteModernObjC.cpp
index 5cd6836..6d79263 100644
--- a/lib/Rewrite/RewriteModernObjC.cpp
+++ b/lib/Rewrite/RewriteModernObjC.cpp
@@ -109,7 +109,7 @@
     llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCSynthesizedStructs;
     llvm::SmallPtrSet<ObjCProtocolDecl*, 8> ObjCSynthesizedProtocols;
     llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCWrittenInterfaces;
-    llvm::SmallPtrSet<TagDecl*, 8> TagsDefinedInIvarDecls;
+    llvm::SmallPtrSet<TagDecl*, 32> GlobalDefinedTags;
     SmallVector<ObjCInterfaceDecl*, 32> ObjCInterfacesSeen;
     /// DefinedNonLazyClasses - List of defined "non-lazy" classes.
     SmallVector<ObjCInterfaceDecl*, 8> DefinedNonLazyClasses;
@@ -346,6 +346,10 @@
                                       std::string &Result);
     
     void RewriteObjCFieldDecl(FieldDecl *fieldDecl, std::string &Result);
+    bool IsTagDefinedInsideClass(ObjCInterfaceDecl *IDecl, TagDecl *Tag,
+                                 bool &IsNamedDefinition);
+    void RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl, 
+                                              std::string &Result);
     
     bool RewriteObjCFieldDeclType(QualType &Type, std::string &Result);
     
@@ -3494,18 +3498,31 @@
   return false;
 }
 
-static bool IsTagDefinedInsideClass(ASTContext *Context,
-                                    ObjCInterfaceDecl *IDecl, TagDecl *Tag) {
+/// IsTagDefinedInsideClass - This routine checks that a named tagged type 
+/// is defined inside an objective-c class. If so, it returns true. 
+bool RewriteModernObjC::IsTagDefinedInsideClass(ObjCInterfaceDecl *IDecl, 
+                                                TagDecl *Tag,
+                                                bool &IsNamedDefinition) {
   if (!IDecl)
     return false;
   SourceLocation TagLocation;
   if (RecordDecl *RD = dyn_cast<RecordDecl>(Tag)) {
     RD = RD->getDefinition();
-    if (!RD)
+    if (!RD || !RD->getDeclName().getAsIdentifierInfo())
       return false;
+    IsNamedDefinition = true;
     TagLocation = RD->getLocation();
     return Context->getSourceManager().isBeforeInTranslationUnit(
-             IDecl->getLocation(), TagLocation);
+                                          IDecl->getLocation(), TagLocation);
+  }
+  if (EnumDecl *ED = dyn_cast<EnumDecl>(Tag)) {
+    if (!ED || !ED->getDeclName().getAsIdentifierInfo())
+      return false;
+    IsNamedDefinition = true;
+    TagLocation = ED->getLocation();
+    return Context->getSourceManager().isBeforeInTranslationUnit(
+                                          IDecl->getLocation(), TagLocation);
+
   }
   return false;
 }
@@ -3529,12 +3546,11 @@
         assert(false && "class not allowed as an ivar type");
       
       Result += RD->getName();
-      if (TagsDefinedInIvarDecls.count(RD)) {
-        // This struct is already defined. Do not write its definition again.
+      if (GlobalDefinedTags.count(RD)) {
+        // struct/union is defined globally, use it.
         Result += " ";
         return true;
       }
-      TagsDefinedInIvarDecls.insert(RD);
       Result += " {\n";
       for (RecordDecl::field_iterator i = RD->field_begin(), 
            e = RD->field_end(); i != e; ++i) {
@@ -3550,12 +3566,11 @@
     if (ED->isCompleteDefinition()) {
       Result += "\n\tenum ";
       Result += ED->getName();
-      if (TagsDefinedInIvarDecls.count(ED)) {
-        // This enum is already defined. Do not write its definition again.
+      if (GlobalDefinedTags.count(ED)) {
+        // Enum is globall defined, use it.
         Result += " ";
         return true;
       }
-      TagsDefinedInIvarDecls.insert(ED);
       
       Result += " {\n";
       for (EnumDecl::enumerator_iterator EC = ED->enumerator_begin(),
@@ -3606,6 +3621,39 @@
   Result += ";\n";
 }
 
+/// RewriteLocallyDefinedNamedAggregates - This routine rewrites locally defined
+/// named aggregate types into the input buffer.
+void RewriteModernObjC::RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl, 
+                                             std::string &Result) {
+  QualType Type = fieldDecl->getType();
+  if (Type->isArrayType())
+    Type = Context->getBaseElementType(Type);
+  ObjCInterfaceDecl *IDecl = 
+    dyn_cast<ObjCInterfaceDecl>(fieldDecl->getDeclContext());
+  
+  TagDecl *TD = 0;
+  if (Type->isRecordType()) {
+    TD = Type->getAs<RecordType>()->getDecl();
+  }
+  else if (Type->isEnumeralType()) {
+    TD = Type->getAs<EnumType>()->getDecl();
+  }
+  
+  if (TD) {
+    if (GlobalDefinedTags.count(TD))
+      return;
+    
+    bool IsNamedDefinition = false;
+    if (IsTagDefinedInsideClass(IDecl, TD, IsNamedDefinition)) {
+      RewriteObjCFieldDeclType(Type, Result);
+      Result += ";";
+    }
+    if (IsNamedDefinition)
+      GlobalDefinedTags.insert(TD);
+  }
+    
+}
+
 /// RewriteObjCInternalStruct - Rewrite one internal struct corresponding to
 /// an objective-c class with ivars.
 void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl,
@@ -3634,6 +3682,12 @@
     return;
   }
   
+  // Insert named struct/union definitions inside class to
+  // outer scope. This follows semantics of locally defined
+  // struct/unions in objective-c classes.
+  for (unsigned i = 0, e = IVars.size(); i < e; i++)
+    RewriteLocallyDefinedNamedAggregates(IVars[i], Result);
+
   Result += "\nstruct ";
   Result += CDecl->getNameAsString();
   Result += "_IMPL {\n";
@@ -3643,7 +3697,7 @@
     Result += "_IMPL "; Result += RCDecl->getNameAsString();
     Result += "_IVARS;\n";
   }
-  TagsDefinedInIvarDecls.clear();
+  
   for (unsigned i = 0, e = IVars.size(); i < e; i++)
     RewriteObjCFieldDecl(IVars[i], Result);
 
@@ -7264,7 +7318,8 @@
 
       if (IvarT->isRecordType()) {
         RecordDecl *RD = IvarT->getAs<RecordType>()->getDecl();
-        if (IsTagDefinedInsideClass(Context, iFaceDecl->getDecl(), RD)) {
+        RD = RD->getDefinition();
+        if (RD && !RD->getDeclName().getAsIdentifierInfo()) {
           // decltype(((Foo_IMPL*)0)->bar) *
           std::string RecName = iFaceDecl->getDecl()->getName();
           RecName += "_IMPL";
diff --git a/test/Rewriter/rewrite-modern-ivar-access.mm b/test/Rewriter/rewrite-modern-ivar-access.mm
new file mode 100644
index 0000000..6599236
--- /dev/null
+++ b/test/Rewriter/rewrite-modern-ivar-access.mm
@@ -0,0 +1,101 @@
+// RUN: %clang_cc1 -fblocks -rewrite-objc -fms-extensions %s -o %t-rw.cpp
+// RUN: %clang_cc1 -Werror -fsyntax-only -Wno-address-of-temporary -Wno-c++11-narrowing -std=c++11 -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
+
+// FIXME: It is incompatible to mingw due to __declspec.
+// XFAIL: mingw32
+
+struct OUTSIDE {
+  int i_OUTSIDE;
+  double d_OUTSIDE;
+};
+
+
+@interface I1 {
+@protected
+  struct OUTSIDE ivar_I1;
+
+  struct INNER_I1 {
+    int i_INNER_I1;
+    double d_INNER_I1;
+  };
+
+  struct INNER_I1 ivar_I2;
+
+  struct OUTSIDE ivar_I3;
+
+  struct {
+    int i_noname;
+    double d_noname;
+  } NONAME_I4;
+
+  struct {
+    int i_noname;
+    double d_noname;
+  } NONAME_I5;
+}
+@end
+
+@implementation I1
+- (void) I1_Meth {
+  ivar_I1.i_OUTSIDE = 0;
+
+  ivar_I2.i_INNER_I1 = 1;
+
+  ivar_I3.i_OUTSIDE = 2;
+
+  NONAME_I4.i_noname = 3;
+
+  NONAME_I5.i_noname = 4;
+}
+@end
+
+@interface INTF2 {
+@protected
+  struct OUTSIDE ivar_INTF2;
+
+  struct {
+    int i_noname;
+    double d_noname;
+  } NONAME_INTF4;
+
+
+  struct OUTSIDE ivar_INTF3;
+
+  struct INNER_I1 ivar_INTF4;
+
+  struct {
+    int i_noname;
+    double d_noname;
+  } NONAME_INTF5;
+
+  struct INNER_INTF2 {
+    int i_INNER_INTF2;
+    double d_INNER_INTF2;
+  };
+
+  struct INNER_INTF2 ivar_INTF6, ivar_INTF7;
+
+  struct INNER_INTF3 {
+    int i;
+  } X1,X2,X3;
+
+}
+@end
+
+@implementation INTF2
+- (void) I2_Meth {
+  ivar_INTF2.i_OUTSIDE = 0;
+
+  ivar_INTF4.i_INNER_I1 = 1;
+
+  ivar_INTF3.i_OUTSIDE = 2;
+
+  NONAME_INTF4.i_noname = 3;
+
+  NONAME_INTF5.i_noname = 4;
+  ivar_INTF6.i_INNER_INTF2 = 5;
+  ivar_INTF7.i_INNER_INTF2 = 5;
+  X1.i = X2.i = X3.i = 1;
+}
+@end
+
diff --git a/test/Rewriter/rewrite-modern-ivars-1.mm b/test/Rewriter/rewrite-modern-ivars-1.mm
index 376d300..f05d809 100644
--- a/test/Rewriter/rewrite-modern-ivars-1.mm
+++ b/test/Rewriter/rewrite-modern-ivars-1.mm
@@ -1,5 +1,5 @@
 // RUN: %clang_cc1 -x objective-c++ -Wno-return-type -fblocks -fms-extensions -rewrite-objc %s -o %t-rw.cpp
-// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
+// RUN: %clang_cc1 -fsyntax-only -Werror -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
 
 @interface NSCheapMutableString {
 @private
@@ -87,3 +87,38 @@
 }
 @end
 
+enum OUTSIDE {
+  yes
+};
+
+@interface MoreEnumTests {
+@private
+    enum INSIDE {
+        no
+    } others;
+
+    enum OUTSIDE meetoo;
+
+    enum {
+       one,
+       two
+    } eu;
+}
+@end
+
+@interface I {
+    enum INSIDE I1;
+    enum OUTSIDE  I2;
+    enum ALSO_INSIDE {
+      maybe
+    } I3;
+
+   enum ALSO_INSIDE I4;
+
+    enum {
+       three,
+       four
+    } I5;
+}
+@end
+
diff --git a/test/Rewriter/rewrite-modern-struct-ivar.mm b/test/Rewriter/rewrite-modern-struct-ivar.mm
index 4a137ae..bb6098c 100644
--- a/test/Rewriter/rewrite-modern-struct-ivar.mm
+++ b/test/Rewriter/rewrite-modern-struct-ivar.mm
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -E %s -o %t.mm
 // RUN: %clang_cc1 -fblocks -rewrite-objc -fms-extensions %t.mm -o %t-rw.cpp 
 // RUN: FileCheck --input-file=%t-rw.cpp %s
-// RUN: %clang_cc1 -fsyntax-only -Wno-address-of-temporary -Wno-c++11-narrowing -std=c++11 -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
+// RUN: %clang_cc1 -fsyntax-only -Werror -Wno-address-of-temporary -Wno-c++11-narrowing -std=c++11 -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp
 
 struct S {
     int i1;
@@ -49,4 +49,4 @@
 @end
 
 // CHECK: (*(decltype(((Foo_IMPL *)0U)->bar) *)((char *)self + OBJC_IVAR_$_Foo$bar)).x = 0;
-// CHECK:  (*(decltype(((Foo_IMPL *)0U)->s) *)((char *)self + OBJC_IVAR_$_Foo$s)).x = 0;
+// CHECK: (*(struct _S *)((char *)self + OBJC_IVAR_$_Foo$s)).x = 0;