Centralize error reporting of improper uses of incomplete types in the
new DiagnoseIncompleteType. It provides additional information about
struct/class/union/enum types when possible, either by pointing to the
forward declaration of that type or by pointing to the definition (if
we're in the process of defining that type). 
Fixes <rdar://problem/6500531>.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@62521 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 40c9a99..fa4301b 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -270,6 +270,11 @@
 
   virtual TypeResult ActOnTypeName(Scope *S, Declarator &D);
 
+  bool DiagnoseIncompleteType(SourceLocation Loc, QualType T, unsigned diag,
+                              SourceRange Range1 = SourceRange(),
+                              SourceRange Range2 = SourceRange(),
+                              QualType PrintType = QualType());
+
   //===--------------------------------------------------------------------===//
   // Symbol table / Decl tracking callbacks: SemaDecl.cpp.
   //
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index cff01c6..fe60496 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -705,10 +705,9 @@
     // C99 6.7.5.3p4: the parameters in a parameter type list in a
     // function declarator that is part of a function definition of
     // that function shall not have incomplete type.
-    if (Param->getType()->isIncompleteType() &&
-        !Param->isInvalidDecl()) {
-      Diag(Param->getLocation(), diag::err_typecheck_decl_incomplete_type)
-        << Param->getType();
+    if (!Param->isInvalidDecl() &&
+        DiagnoseIncompleteType(Param->getLocation(), Param->getType(),
+                               diag::err_typecheck_decl_incomplete_type)) {
       Param->setInvalidDecl();
       HasInvalidParm = true;
     }
@@ -2503,10 +2502,10 @@
     // no linkage (C99 6.2.2p6), the type for the object shall be complete...
     if (IDecl->isBlockVarDecl() && 
         IDecl->getStorageClass() != VarDecl::Extern) {
-      if (T->isIncompleteType() && !IDecl->isInvalidDecl()) {
-        Diag(IDecl->getLocation(), diag::err_typecheck_decl_incomplete_type)<<T;
+      if (!IDecl->isInvalidDecl() &&
+          DiagnoseIncompleteType(IDecl->getLocation(), T, 
+                                 diag::err_typecheck_decl_incomplete_type))
         IDecl->setInvalidDecl();
-      }
     }
     // File scope. C99 6.9.2p2: A declaration of an identifier for and 
     // object that has file scope without an initializer, and without a
@@ -2517,13 +2516,13 @@
       if (T->isIncompleteArrayType()) {
         // C99 6.9.2 (p2, p5): Implicit initialization causes an incomplete
         // array to be completed. Don't issue a diagnostic.
-      } else if (T->isIncompleteType() && !IDecl->isInvalidDecl()) {
+      } else if (!IDecl->isInvalidDecl() &&
+                 DiagnoseIncompleteType(IDecl->getLocation(), T,
+                                        diag::err_typecheck_decl_incomplete_type))
         // C99 6.9.2p3: If the declaration of an identifier for an object is
         // a tentative definition and has internal linkage (C99 6.2.2p3), the  
         // declared type shall not be an incomplete type.
-        Diag(IDecl->getLocation(), diag::err_typecheck_decl_incomplete_type)<<T;
         IDecl->setInvalidDecl();
-      }
     }
     if (IDecl->isFileVarDecl())
       CheckForFileScopedRedefinitions(S, IDecl);
@@ -3382,7 +3381,8 @@
     // C99 6.7.2.1p2 - A field may not be an incomplete type except...
     if (FDTy->isIncompleteType()) {
       if (!Record) {  // Incomplete ivar type is always an error.
-        Diag(FD->getLocation(), diag::err_field_incomplete) <<FD->getDeclName();
+        DiagnoseIncompleteType(FD->getLocation(), FD->getType(), 
+                               diag::err_field_incomplete);
         FD->setInvalidDecl();
         EnclosingDecl->setInvalidDecl();
         continue;
@@ -3390,7 +3390,8 @@
       if (i != NumFields-1 ||                   // ... that the last member ...
           !Record->isStruct() ||  // ... of a structure ...
           !FDTy->isArrayType()) {         //... may have incomplete array type.
-        Diag(FD->getLocation(), diag::err_field_incomplete) <<FD->getDeclName();
+        DiagnoseIncompleteType(FD->getLocation(), FD->getType(), 
+                               diag::err_field_incomplete);
         FD->setInvalidDecl();
         EnclosingDecl->setInvalidDecl();
         continue;
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 33a31c7..95f9200 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -339,8 +339,9 @@
   // C++ [class.derived]p2:
   //   The class-name in a base-specifier shall not be an incompletely
   //   defined class.
-  if (BaseType->isIncompleteType())
-    return Diag(BaseLoc, diag::err_incomplete_base_class) << SpecifierRange;
+  if (DiagnoseIncompleteType(BaseLoc, BaseType, diag::err_incomplete_base_class,
+                             SpecifierRange))
+    return true;
 
   // If the base class is polymorphic, the new one is, too.
   RecordDecl *BaseDecl = BaseType->getAsRecordType()->getDecl();
@@ -2179,17 +2180,19 @@
   // incomplete type, other than [cv] void*.
   QualType BaseType = ExDeclType;
   int Mode = 0; // 0 for direct type, 1 for pointer, 2 for reference
+  unsigned DK = diag::err_catch_incomplete;
   if (const PointerType *Ptr = BaseType->getAsPointerType()) {
     BaseType = Ptr->getPointeeType();
     Mode = 1;
+    DK = diag::err_catch_incomplete_ptr;
   } else if(const ReferenceType *Ref = BaseType->getAsReferenceType()) {
     BaseType = Ref->getPointeeType();
     Mode = 2;
+    DK = diag::err_catch_incomplete_ref;
   }
-  if ((Mode == 0 || !BaseType->isVoidType()) && BaseType->isIncompleteType()) {
+  if ((Mode == 0 || !BaseType->isVoidType()) && 
+      DiagnoseIncompleteType(Begin, BaseType, DK))
     Invalid = true;
-    Diag(Begin, diag::err_catch_incomplete) << BaseType << Mode;
-  }
 
   // FIXME: Need to test for ability to copy-construct and destroy the
   // exception variable.
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index be87ee9..fb83cab 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -1021,10 +1021,11 @@
   else if (exprType->isVoidType())
     Diag(OpLoc, diag::ext_sizeof_void_type)
       << (isSizeof ? "sizeof" : "__alignof") << ExprRange;
-  else if (exprType->isIncompleteType())
-    return Diag(OpLoc, isSizeof ? diag::err_sizeof_incomplete_type : 
-                                  diag::err_alignof_incomplete_type)
-      << exprType << ExprRange;
+  else 
+    return DiagnoseIncompleteType(OpLoc, exprType,
+                                  isSizeof ? diag::err_sizeof_incomplete_type : 
+                                             diag::err_alignof_incomplete_type,
+                                  ExprRange);
 
   return false;
 }
@@ -1466,9 +1467,11 @@
   // of the ObjC 'id' struct.
   if (const RecordType *RTy = BaseType->getAsRecordType()) {
     RecordDecl *RDecl = RTy->getDecl();
-    if (RTy->isIncompleteType())
-      return ExprError(Diag(OpLoc, diag::err_typecheck_incomplete_tag)
-               << RDecl->getDeclName() << BaseExpr->getSourceRange());
+    if (DiagnoseIncompleteType(OpLoc, BaseType, 
+                               diag::err_typecheck_incomplete_tag,
+                               BaseExpr->getSourceRange()))
+      return ExprError();
+
     // The record definition is complete, now make sure the member is valid.
     // FIXME: Qualified name lookup for C++ is a bit more complicated
     // than this.
@@ -1906,11 +1909,10 @@
     if (literalType->isVariableArrayType())
       return Diag(LParenLoc, diag::err_variable_object_no_init)
         << SourceRange(LParenLoc, literalExpr->getSourceRange().getEnd());
-  } else if (literalType->isIncompleteType()) {
-    return Diag(LParenLoc, diag::err_typecheck_decl_incomplete_type)
-      << literalType
-      << SourceRange(LParenLoc, literalExpr->getSourceRange().getEnd());
-  }
+  } else if (DiagnoseIncompleteType(LParenLoc, literalType,
+                                    diag::err_typecheck_decl_incomplete_type,
+                SourceRange(LParenLoc, literalExpr->getSourceRange().getEnd())))
+    return true;
 
   if (CheckInitializerTypes(literalExpr, literalType, LParenLoc, 
                             DeclarationName(), /*FIXME:DirectInit=*/false))
@@ -2643,10 +2645,16 @@
         if (PTy->getPointeeType()->isVoidType()) {
           Diag(Loc, diag::ext_gnu_void_ptr)
             << lex->getSourceRange() << rex->getSourceRange();
-        } else {
-          Diag(Loc, diag::err_typecheck_arithmetic_incomplete_type)
+        } else if (PTy->getPointeeType()->isFunctionType()) {
+          Diag(Loc, diag::err_typecheck_pointer_arith_function_type)
             << lex->getType() << lex->getSourceRange();
           return QualType();
+        } else {
+          DiagnoseIncompleteType(Loc, PTy->getPointeeType(), 
+                                 diag::err_typecheck_arithmetic_incomplete_type,
+                                 lex->getSourceRange(), SourceRange(),
+                                 lex->getType());
+          return QualType();
         }
       }
       return PExp->getType();
@@ -3038,9 +3046,9 @@
     break;
   case Expr::MLV_IncompleteType:
   case Expr::MLV_IncompleteVoidType:
-    Diag = diag::err_typecheck_incomplete_type_not_modifiable_lvalue;
-    NeedType = true;
-    break;
+    return S.DiagnoseIncompleteType(Loc, E->getType(), 
+                      diag::err_typecheck_incomplete_type_not_modifiable_lvalue,
+                                    E->getSourceRange());
   case Expr::MLV_DuplicateVectorComponents:
     Diag = diag::err_typecheck_duplicate_vector_components_not_mlvalue;
     break;
@@ -3155,10 +3163,16 @@
     } else if (PT->getPointeeType()->isVoidType()) {
       // Pointer to void is extension.
       Diag(OpLoc, diag::ext_gnu_void_ptr) << Op->getSourceRange();
-    } else {
-      Diag(OpLoc, diag::err_typecheck_arithmetic_incomplete_type)
+    } else if (PT->getPointeeType()->isFunctionType()) {
+      Diag(OpLoc, diag::err_typecheck_pointer_arith_function_type)
         << ResType << Op->getSourceRange();
       return QualType();
+    } else {
+      DiagnoseIncompleteType(OpLoc, PT->getPointeeType(), 
+                             diag::err_typecheck_arithmetic_incomplete_type,
+                             Op->getSourceRange(), SourceRange(),
+                             ResType);
+      return QualType();
     }
   } else if (ResType->isComplexType()) {
     // C99 does not support ++/-- on complex types, we allow as an extension.
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index 09db18e..8958221 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -172,9 +172,10 @@
   //
   if (Ty->isArrayType())
     return Diag(TyBeginLoc, diag::err_value_init_for_array_type) << FullRange;
-  if (!Ty->isDependentType() && Ty->isIncompleteType() && !Ty->isVoidType())
-    return Diag(TyBeginLoc, diag::err_invalid_incomplete_type_use) 
-             << Ty << FullRange;
+  if (!Ty->isDependentType() && !Ty->isVoidType() &&
+      DiagnoseIncompleteType(TyBeginLoc, Ty, 
+                             diag::err_invalid_incomplete_type_use, FullRange))
+    return true;
 
   return new CXXZeroInitValueExpr(Ty, TyBeginLoc, RParenLoc);
 }
@@ -578,9 +579,10 @@
   }
 
   QualType Pointee = Type->getAsPointerType()->getPointeeType();
-  if (Pointee->isIncompleteType() && !Pointee->isVoidType())
-    Diag(StartLoc, diag::warn_delete_incomplete)
-      << Pointee << Ex->getSourceRange();
+  if (!Pointee->isVoidType() && 
+      DiagnoseIncompleteType(StartLoc, Pointee, diag::warn_delete_incomplete,
+                             Ex->getSourceRange()))
+    return true;
   else if (!Pointee->isObjectType()) {
     Diag(StartLoc, diag::err_delete_operand)
       << Type << Ex->getSourceRange();
diff --git a/lib/Sema/SemaNamedCast.cpp b/lib/Sema/SemaNamedCast.cpp
index 3d3572d..06fc9d8 100644
--- a/lib/Sema/SemaNamedCast.cpp
+++ b/lib/Sema/SemaNamedCast.cpp
@@ -685,11 +685,10 @@
   if (DestPointee->isVoidType()) {
     assert(DestPointer && "Reference to void is not possible");
   } else if (DestRecord) {
-    if (!DestRecord->getDecl()->isDefinition()) {
-      Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_incomplete)
-        << DestPointee.getUnqualifiedType() << DestRange;
+    if (Self.DiagnoseIncompleteType(OpRange.getBegin(), DestPointee, 
+                                    diag::err_bad_dynamic_cast_incomplete,
+                                    DestRange))
       return;
-    }
   } else {
     Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class)
       << DestPointee.getUnqualifiedType() << DestRange;
@@ -720,11 +719,10 @@
 
   const RecordType *SrcRecord = SrcPointee->getAsRecordType();
   if (SrcRecord) {
-    if (!SrcRecord->getDecl()->isDefinition()) {
-      Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_incomplete)
-        << SrcPointee.getUnqualifiedType() << SrcExpr->getSourceRange();
+    if (Self.DiagnoseIncompleteType(OpRange.getBegin(), SrcPointee,
+                                    diag::err_bad_dynamic_cast_incomplete,
+                                    SrcExpr->getSourceRange()))
       return;
-    }
   } else {
     Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class)
       << SrcPointee.getUnqualifiedType() << SrcExpr->getSourceRange();
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index 8a459f4..ecae793 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -352,9 +352,8 @@
 
       // C99 6.7.5.2p1: If the element type is an incomplete or function type, 
       // reject it (e.g. void ary[7], struct foo ary[7], void ary[7]())
-      if (T->isIncompleteType()) { 
-        Diag(D.getIdentifierLoc(), diag::err_illegal_decl_array_incomplete_type)
-          << T;
+      if (DiagnoseIncompleteType(D.getIdentifierLoc(), T, 
+                                 diag::err_illegal_decl_array_incomplete_type)) {
         T = Context.IntTy;
         D.setInvalidType(true);
       } else if (T->isFunctionType()) {
@@ -690,4 +689,67 @@
   }
 }
 
+/// @brief If the type T is incomplete and cannot be completed,
+/// produce a suitable diagnostic.
+///
+/// This routine checks whether the type @p T is complete in any
+/// context where a complete type is required. If @p T is a complete
+/// type, returns false. If @p T is incomplete, issues the diagnostic
+/// @p diag (giving it the type @p T) and returns true.
+///
+/// @param Loc  The location in the source that the incomplete type
+/// diagnostic should refer to.
+///
+/// @param T  The type that this routine is examining for completeness.
+///
+/// @param diag The diagnostic value (e.g., 
+/// @c diag::err_typecheck_decl_incomplete_type) that will be used
+/// for the error message if @p T is incomplete.
+///
+/// @param Range1  An optional range in the source code that will be a
+/// part of the "incomplete type" error message.
+///
+/// @param Range2  An optional range in the source code that will be a
+/// part of the "incomplete type" error message.
+///
+/// @param PrintType If non-NULL, the type that should be printed
+/// instead of @p T. This parameter should be used when the type that
+/// we're checking for incompleteness isn't the type that should be
+/// displayed to the user, e.g., when T is a type and PrintType is a
+/// pointer to T.
+///
+/// @returns @c true if @p T is incomplete and a diagnostic was emitted,
+/// @c false otherwise.
+///
+/// @todo When Clang gets proper support for C++ templates, this
+/// routine will also be able perform template instantiation when @p T
+/// is a class template specialization.
+bool Sema::DiagnoseIncompleteType(SourceLocation Loc, QualType T, unsigned diag,
+                                  SourceRange Range1, SourceRange Range2,
+                                  QualType PrintType) {
+  // If we have a complete type, we're done.
+  if (!T->isIncompleteType())
+    return false;
 
+  if (PrintType.isNull())
+    PrintType = T;
+
+  // We have an incomplete type. Produce a diagnostic.
+  Diag(Loc, diag) << PrintType << Range1 << Range2;
+
+  // If the type was a forward declaration of a class/struct/union
+  // type, produce 
+  const TagType *Tag = 0;
+  if (const RecordType *Record = T->getAsRecordType())
+    Tag = Record;
+  else if (const EnumType *Enum = T->getAsEnumType())
+    Tag = Enum;
+
+  if (Tag && !Tag->getDecl()->isInvalidDecl())
+    Diag(Tag->getDecl()->getLocation(), 
+         Tag->isBeingDefined() ? diag::note_type_being_defined
+                               : diag::note_forward_declaration)
+        << QualType(Tag, 0);
+
+  return true;
+}