#include "clang/Sema/Sema.h"
namespace clang {
static CountAttributedType::DynamicCountPointerKind
getCountAttrKind(bool CountInBytes, bool OrNull) {
if (CountInBytes)
return OrNull ? CountAttributedType::SizedByOrNull
: CountAttributedType::SizedBy;
return OrNull ? CountAttributedType::CountedByOrNull
: CountAttributedType::CountedBy;
}
static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) {
const auto *RD = FD->getParent();
while (RD && (RD->isAnonymousStructOrUnion() ||
(!RD->isCompleteDefinition() && RD->getName().empty()))) {
const auto *Parent = dyn_cast<RecordDecl>(RD->getParent());
if (!Parent)
break;
RD = Parent;
}
return RD;
}
enum class CountedByInvalidPointeeTypeKind {
INCOMPLETE,
SIZELESS,
FUNCTION,
FLEXIBLE_ARRAY_MEMBER,
VALID,
};
bool Sema::CheckCountedByAttrOnField(
FieldDecl *FD, Expr *E,
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls, bool CountInBytes,
bool OrNull) {
unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
if (FD->getParent()->isUnion()) {
Diag(FD->getBeginLoc(), diag::err_count_attr_in_union)
<< Kind << FD->getSourceRange();
return true;
}
const auto FieldTy = FD->getType();
if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
Diag(FD->getBeginLoc(),
diag::err_count_attr_not_on_ptr_or_flexible_array_member)
<< Kind << FD->getLocation() << 1;
return true;
}
if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) {
Diag(FD->getBeginLoc(),
diag::err_count_attr_not_on_ptr_or_flexible_array_member)
<< Kind << FD->getLocation() << 0;
return true;
}
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
LangOptions::StrictFlexArraysLevelKind::IncompleteOnly;
if (FieldTy->isArrayType() &&
!Decl::isFlexibleArrayMemberLike(getASTContext(), FD, FieldTy,
StrictFlexArraysLevel, true)) {
Diag(FD->getBeginLoc(),
diag::err_counted_by_attr_on_array_not_flexible_array_member)
<< Kind << FD->getLocation();
return true;
}
CountedByInvalidPointeeTypeKind InvalidTypeKind =
CountedByInvalidPointeeTypeKind::VALID;
QualType PointeeTy;
int SelectPtrOrArr = 0;
if (FieldTy->isPointerType()) {
PointeeTy = FieldTy->getPointeeType();
SelectPtrOrArr = 0;
} else {
assert(FieldTy->isArrayType());
const ArrayType *AT = getASTContext().getAsArrayType(FieldTy);
PointeeTy = AT->getElementType();
SelectPtrOrArr = 1;
}
bool ShouldWarn = false;
if (PointeeTy->isIncompleteType() && !CountInBytes) {
InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;
} else if (PointeeTy->isSizelessType()) {
InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;
} else if (PointeeTy->isFunctionType()) {
InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION;
} else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) {
if (FieldTy->isArrayType() && !getLangOpts().BoundsSafety) {
ShouldWarn = true;
}
InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;
}
if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {
unsigned DiagID = ShouldWarn
? diag::warn_counted_by_attr_elt_type_unknown_size
: diag::err_counted_by_attr_pointee_unknown_size;
Diag(FD->getBeginLoc(), DiagID)
<< SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind
<< (ShouldWarn ? 1 : 0) << Kind << FD->getSourceRange();
return true;
}
if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)
<< Kind << E->getSourceRange();
return true;
}
auto *DRE = dyn_cast<DeclRefExpr>(E);
if (!DRE) {
Diag(E->getBeginLoc(),
diag::err_count_attr_only_support_simple_decl_reference)
<< Kind << E->getSourceRange();
return true;
}
auto *CountDecl = DRE->getDecl();
FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl);
if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) {
CountFD = IFD->getAnonField();
}
if (!CountFD) {
Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)
<< CountDecl << Kind << E->getSourceRange();
Diag(CountDecl->getBeginLoc(),
diag::note_flexible_array_counted_by_attr_field)
<< CountDecl << CountDecl->getSourceRange();
return true;
}
if (FD->getParent() != CountFD->getParent()) {
if (CountFD->getParent()->isUnion()) {
Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union)
<< Kind << CountFD->getSourceRange();
return true;
}
auto *RD = GetEnclosingNamedOrTopAnonRecord(FD);
auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD);
if (RD != CountRD) {
Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)
<< CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();
Diag(CountFD->getBeginLoc(),
diag::note_flexible_array_counted_by_attr_field)
<< CountFD << CountFD->getSourceRange();
return true;
}
}
Decls.push_back(TypeCoupledDeclRefInfo(CountFD, false));
return false;
}
}