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;
+}