[libclang] Expose record layout info via new libclang functions:

clang_Type_getAlignOf
clang_Type_getSizeOf
clang_Type_getOffsetOf
clang_Cursor_isBitField

Patch by Loïc Jaquemet!

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179251 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index ce9e491..e97cb30 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -1144,6 +1144,61 @@
   return CXChildVisit_Recurse;
 }
 
+static enum CXChildVisitResult PrintTypeSize(CXCursor cursor, CXCursor p,
+                                             CXClientData d) {
+  CXType T;
+  enum CXCursorKind K = clang_getCursorKind(cursor);
+  if (clang_isInvalid(K))
+    return CXChildVisit_Recurse;
+  T = clang_getCursorType(cursor);
+  PrintCursor(cursor, NULL);
+  PrintTypeAndTypeKind(T, " [type=%s] [typekind=%s]");
+  /* Print the type sizeof if applicable. */
+  {
+    long long Size = clang_Type_getSizeOf(T);
+    if (Size >= 0 || Size < -1 ) {
+      printf(" [sizeof=%lld]", Size);
+    }
+  }
+  /* Print the type alignof if applicable. */
+  {
+    long long Align = clang_Type_getAlignOf(T);
+    if (Align >= 0 || Align < -1) {
+      printf(" [alignof=%lld]", Align);
+    }
+  }
+  /* Print the record field offset if applicable. */
+  {
+    const char *FieldName = clang_getCString(clang_getCursorSpelling(cursor));
+    /* recurse to get the root anonymous record parent */
+    CXCursor Parent, Root;
+    if (clang_getCursorKind(cursor) == CXCursor_FieldDecl ) {
+      const char *RootParentName;
+      Root = Parent = p;
+      do {
+        Root = Parent;
+        RootParentName = clang_getCString(clang_getCursorSpelling(Root));
+        Parent = clang_getCursorSemanticParent(Root);
+      } while ( clang_getCursorType(Parent).kind == CXType_Record &&
+                !strcmp(RootParentName, "") );
+      /* if RootParentName is "", record is anonymous. */
+      {
+        long long Offset = clang_Type_getOffsetOf(clang_getCursorType(Root),
+                                                  FieldName);
+        printf(" [offsetof=%lld]", Offset);
+      }
+    }
+  }
+  /* Print if its a bitfield */
+  {
+    int IsBitfield = clang_Cursor_isBitField(cursor);
+    if (IsBitfield)
+      printf(" [BitFieldSize=%d]", clang_getFieldDeclBitWidth(cursor));
+  }
+  printf("\n");
+  return CXChildVisit_Recurse;
+}
+
 /******************************************************************************/
 /* Bitwidth testing.                                                          */
 /******************************************************************************/
@@ -3642,6 +3697,7 @@
   fprintf(stderr,
     "       c-index-test -test-print-linkage-source {<args>}*\n"
     "       c-index-test -test-print-type {<args>}*\n"
+    "       c-index-test -test-print-type-size {<args>}*\n"
     "       c-index-test -test-print-bitwidth {<args>}*\n"
     "       c-index-test -print-usr [<CursorKind> {<args>}]*\n"
     "       c-index-test -print-usr-file <file>\n"
@@ -3728,6 +3784,9 @@
   else if (argc > 2 && strcmp(argv[1], "-test-print-type") == 0)
     return perform_test_load_source(argc - 2, argv + 2, "all",
                                     PrintType, 0);
+  else if (argc > 2 && strcmp(argv[1], "-test-print-type-size") == 0)
+    return perform_test_load_source(argc - 2, argv + 2, "all",
+                                    PrintTypeSize, 0);
   else if (argc > 2 && strcmp(argv[1], "-test-print-bitwidth") == 0)
     return perform_test_load_source(argc - 2, argv + 2, "all",
                                     PrintBitWidth, 0);
diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp
index 6f87fc5..16009bf 100644
--- a/tools/libclang/CXType.cpp
+++ b/tools/libclang/CXType.cpp
@@ -651,6 +651,126 @@
   return result;
 }
 
+long long clang_Type_getAlignOf(CXType T) {
+  if (T.kind == CXType_Invalid)
+    return CXTypeLayoutError_Invalid;
+  ASTContext &Ctx = cxtu::getASTUnit(GetTU(T))->getASTContext();
+  QualType QT = GetQualType(T);
+  // [expr.alignof] p1: return size_t value for complete object type, reference
+  //                    or array.
+  // [expr.alignof] p3: if reference type, return size of referenced type
+  if (QT->isReferenceType())
+    QT = QT.getNonReferenceType();
+  if (QT->isIncompleteType())
+    return CXTypeLayoutError_Incomplete;
+  if (QT->isDependentType())
+    return CXTypeLayoutError_Dependent;
+  // Exceptions by GCC extension - see ASTContext.cpp:1313 getTypeInfoImpl
+  // if (QT->isFunctionType()) return 4; // Bug #15511 - should be 1
+  // if (QT->isVoidType()) return 1;
+  return Ctx.getTypeAlignInChars(QT).getQuantity();
+}
+
+long long clang_Type_getSizeOf(CXType T) {
+  if (T.kind == CXType_Invalid)
+    return CXTypeLayoutError_Invalid;
+  ASTContext &Ctx = cxtu::getASTUnit(GetTU(T))->getASTContext();
+  QualType QT = GetQualType(T);
+  // [expr.sizeof] p2: if reference type, return size of referenced type
+  if (QT->isReferenceType())
+    QT = QT.getNonReferenceType();
+  // [expr.sizeof] p1: return -1 on: func, incomplete, bitfield, incomplete
+  //                   enumeration
+  // Note: We get the cxtype, not the cxcursor, so we can't call
+  //       FieldDecl->isBitField()
+  // [expr.sizeof] p3: pointer ok, function not ok.
+  // [gcc extension] lib/AST/ExprConstant.cpp:1372 HandleSizeof : vla == error
+  if (QT->isIncompleteType())
+    return CXTypeLayoutError_Incomplete;
+  if (QT->isDependentType())
+    return CXTypeLayoutError_Dependent;
+  if (!QT->isConstantSizeType())
+    return CXTypeLayoutError_NotConstantSize;
+  // [gcc extension] lib/AST/ExprConstant.cpp:1372
+  //                 HandleSizeof : {voidtype,functype} == 1
+  // not handled by ASTContext.cpp:1313 getTypeInfoImpl
+  if (QT->isVoidType() || QT->isFunctionType())
+    return 1;
+  return Ctx.getTypeSizeInChars(QT).getQuantity();
+}
+
+static long long visitRecordForValidation(const RecordDecl *RD) {
+  for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
+       I != E; ++I){
+    QualType FQT = (*I)->getType();
+    if (FQT->isIncompleteType())
+      return CXTypeLayoutError_Incomplete;
+    if (FQT->isDependentType())
+      return CXTypeLayoutError_Dependent;
+    // recurse
+    if (const RecordType *ChildType = (*I)->getType()->getAs<RecordType>()) {
+      if (const RecordDecl *Child = ChildType->getDecl()) {
+        long long ret = visitRecordForValidation(Child);
+        if (ret < 0)
+          return ret;
+      }
+    }
+    // else try next field
+  }
+  return 0;
+}
+
+long long clang_Type_getOffsetOf(CXType PT, const char *S) {
+  // check that PT is not incomplete/dependent
+  CXCursor PC = clang_getTypeDeclaration(PT);
+  if (clang_isInvalid(PC.kind))
+    return CXTypeLayoutError_Invalid;
+  const RecordDecl *RD =
+        dyn_cast_or_null<RecordDecl>(cxcursor::getCursorDecl(PC));
+  if (!RD)
+    return CXTypeLayoutError_Invalid;
+  RD = RD->getDefinition();
+  if (!RD)
+    return CXTypeLayoutError_Incomplete;
+  QualType RT = GetQualType(PT);
+  if (RT->isIncompleteType())
+    return CXTypeLayoutError_Incomplete;
+  if (RT->isDependentType())
+    return CXTypeLayoutError_Dependent;
+  // We recurse into all record fields to detect incomplete and dependent types.
+  long long Error = visitRecordForValidation(RD);
+  if (Error < 0)
+    return Error;
+  if (!S)
+    return CXTypeLayoutError_InvalidFieldName;
+  // lookup field
+  ASTContext &Ctx = cxtu::getASTUnit(GetTU(PT))->getASTContext();
+  IdentifierInfo *II = &Ctx.Idents.get(S);
+  DeclarationName FieldName(II);
+  RecordDecl::lookup_const_result Res = RD->lookup(FieldName);
+  // If a field of the parent record is incomplete, lookup will fail.
+  // and we would return InvalidFieldName instead of Incomplete.
+  // But this erroneous results does protects again a hidden assertion failure
+  // in the RecordLayoutBuilder
+  if (Res.size() != 1)
+    return CXTypeLayoutError_InvalidFieldName;
+  if (const FieldDecl *FD = dyn_cast<FieldDecl>(Res.front()))
+    return Ctx.getFieldOffset(FD);
+  if (const IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(Res.front()))
+    return Ctx.getFieldOffset(IFD);
+  // we don't want any other Decl Type.
+  return CXTypeLayoutError_InvalidFieldName;
+}
+
+unsigned clang_Cursor_isBitField(CXCursor C) {
+  if (!clang_isDeclaration(C.kind))
+    return 0;
+  const FieldDecl *FD = dyn_cast_or_null<FieldDecl>(cxcursor::getCursorDecl(C));
+  if (!FD)
+    return 0;
+  return FD->isBitField();
+}
+
 CXString clang_getDeclObjCTypeEncoding(CXCursor C) {
   if (!clang_isDeclaration(C.kind))
     return cxstring::createEmpty();
diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports
index d99f24e..1f179f1 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -14,6 +14,7 @@
 clang_Cursor_getSpellingNameRange
 clang_Cursor_getTranslationUnit
 clang_Cursor_getReceiverType
+clang_Cursor_isBitField
 clang_Cursor_isDynamicCall
 clang_Cursor_isNull
 clang_Cursor_getModule
@@ -53,6 +54,9 @@
 clang_TParamCommandComment_isParamPositionValid
 clang_TParamCommandComment_getDepth
 clang_TParamCommandComment_getIndex
+clang_Type_getAlignOf
+clang_Type_getSizeOf
+clang_Type_getOffsetOf
 clang_VerbatimBlockLineComment_getText
 clang_VerbatimLineComment_getText
 clang_HTMLTagComment_getAsString