[lldb][TypeSystemClang][NFCI] Factor completion logic for individual types out of GetCompleteQualType (#95402)

This patch factors out the completion logic for individual clang::Type's
into their own helper functions.

During the process I cleaned up a few assumptions (e.g., unnecessary
if-guards that could be asserts because these conditions are guaranteed
by the `clang::Type::TypeClass` switch in `GetCompleteQualType`).

This is mainly motivated by the type-completion rework proposed in
https://github.com/llvm/llvm-project/pull/95100.
This commit is contained in:
Michael Buch
2024-06-14 07:20:50 +01:00
committed by GitHub
parent b1de42a81d
commit f2d215f572

View File

@@ -2574,6 +2574,128 @@ TypeSystemClang::GetDeclContextForType(clang::QualType type) {
return nullptr;
}
/// Returns the clang::RecordType of the specified \ref qual_type. This
/// function will try to complete the type if necessary (and allowed
/// by the specified \ref allow_completion). If we fail to return a *complete*
/// type, returns nullptr.
static const clang::RecordType *GetCompleteRecordType(clang::ASTContext *ast,
clang::QualType qual_type,
bool allow_completion) {
assert(qual_type->isRecordType());
const auto *tag_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr());
clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl();
// RecordType with no way of completing it, return the plain
// TagType.
if (!cxx_record_decl || !cxx_record_decl->hasExternalLexicalStorage())
return tag_type;
const bool is_complete = cxx_record_decl->isCompleteDefinition();
const bool fields_loaded =
cxx_record_decl->hasLoadedFieldsFromExternalStorage();
// Already completed this type, nothing to be done.
if (is_complete && fields_loaded)
return tag_type;
if (!allow_completion)
return nullptr;
// Call the field_begin() accessor to for it to use the external source
// to load the fields...
//
// TODO: if we need to complete the type but have no external source,
// shouldn't we error out instead?
clang::ExternalASTSource *external_ast_source = ast->getExternalSource();
if (external_ast_source) {
external_ast_source->CompleteType(cxx_record_decl);
if (cxx_record_decl->isCompleteDefinition()) {
cxx_record_decl->field_begin();
cxx_record_decl->setHasLoadedFieldsFromExternalStorage(true);
}
}
return tag_type;
}
/// Returns the clang::EnumType of the specified \ref qual_type. This
/// function will try to complete the type if necessary (and allowed
/// by the specified \ref allow_completion). If we fail to return a *complete*
/// type, returns nullptr.
static const clang::EnumType *GetCompleteEnumType(clang::ASTContext *ast,
clang::QualType qual_type,
bool allow_completion) {
assert(qual_type->isEnumeralType());
assert(ast);
const clang::EnumType *enum_type =
llvm::cast<clang::EnumType>(qual_type.getTypePtr());
auto *tag_decl = enum_type->getAsTagDecl();
assert(tag_decl);
// Already completed, nothing to be done.
if (tag_decl->getDefinition())
return enum_type;
if (!allow_completion)
return nullptr;
// No definition but can't complete it, error out.
if (!tag_decl->hasExternalLexicalStorage())
return nullptr;
// We can't complete the type without an external source.
clang::ExternalASTSource *external_ast_source = ast->getExternalSource();
if (!external_ast_source)
return nullptr;
external_ast_source->CompleteType(tag_decl);
return enum_type;
}
/// Returns the clang::ObjCObjectType of the specified \ref qual_type. This
/// function will try to complete the type if necessary (and allowed
/// by the specified \ref allow_completion). If we fail to return a *complete*
/// type, returns nullptr.
static const clang::ObjCObjectType *
GetCompleteObjCObjectType(clang::ASTContext *ast, QualType qual_type,
bool allow_completion) {
assert(qual_type->isObjCObjectType());
assert(ast);
const clang::ObjCObjectType *objc_class_type =
llvm::cast<clang::ObjCObjectType>(qual_type);
clang::ObjCInterfaceDecl *class_interface_decl =
objc_class_type->getInterface();
// We currently can't complete objective C types through the newly added
// ASTContext because it only supports TagDecl objects right now...
if (!class_interface_decl)
return objc_class_type;
// Already complete, nothing to be done.
if (class_interface_decl->getDefinition())
return objc_class_type;
if (!allow_completion)
return nullptr;
// No definition but can't complete it, error out.
if (!class_interface_decl->hasExternalLexicalStorage())
return nullptr;
// We can't complete the type without an external source.
clang::ExternalASTSource *external_ast_source = ast->getExternalSource();
if (!external_ast_source)
return nullptr;
external_ast_source->CompleteType(class_interface_decl);
return objc_class_type;
}
static bool GetCompleteQualType(clang::ASTContext *ast,
clang::QualType qual_type,
bool allow_completion = true) {
@@ -2591,92 +2713,26 @@ static bool GetCompleteQualType(clang::ASTContext *ast,
allow_completion);
} break;
case clang::Type::Record: {
clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl();
if (cxx_record_decl) {
if (cxx_record_decl->hasExternalLexicalStorage()) {
const bool is_complete = cxx_record_decl->isCompleteDefinition();
const bool fields_loaded =
cxx_record_decl->hasLoadedFieldsFromExternalStorage();
if (is_complete && fields_loaded)
return true;
if (const auto *RT =
GetCompleteRecordType(ast, qual_type, allow_completion))
return !RT->isIncompleteType();
if (!allow_completion)
return false;
// Call the field_begin() accessor to for it to use the external source
// to load the fields...
clang::ExternalASTSource *external_ast_source =
ast->getExternalSource();
if (external_ast_source) {
external_ast_source->CompleteType(cxx_record_decl);
if (cxx_record_decl->isCompleteDefinition()) {
cxx_record_decl->field_begin();
cxx_record_decl->setHasLoadedFieldsFromExternalStorage(true);
}
}
}
}
const clang::TagType *tag_type =
llvm::cast<clang::TagType>(qual_type.getTypePtr());
return !tag_type->isIncompleteType();
return false;
} break;
case clang::Type::Enum: {
const clang::TagType *tag_type =
llvm::dyn_cast<clang::TagType>(qual_type.getTypePtr());
if (tag_type) {
clang::TagDecl *tag_decl = tag_type->getDecl();
if (tag_decl) {
if (tag_decl->getDefinition())
return true;
if (!allow_completion)
return false;
if (tag_decl->hasExternalLexicalStorage()) {
if (ast) {
clang::ExternalASTSource *external_ast_source =
ast->getExternalSource();
if (external_ast_source) {
external_ast_source->CompleteType(tag_decl);
return !tag_type->isIncompleteType();
}
}
}
return false;
}
}
if (const auto *ET = GetCompleteEnumType(ast, qual_type, allow_completion))
return !ET->isIncompleteType();
return false;
} break;
case clang::Type::ObjCObject:
case clang::Type::ObjCInterface: {
const clang::ObjCObjectType *objc_class_type =
llvm::dyn_cast<clang::ObjCObjectType>(qual_type);
if (objc_class_type) {
clang::ObjCInterfaceDecl *class_interface_decl =
objc_class_type->getInterface();
// We currently can't complete objective C types through the newly added
// ASTContext because it only supports TagDecl objects right now...
if (class_interface_decl) {
if (class_interface_decl->getDefinition())
return true;
if (const auto *OT =
GetCompleteObjCObjectType(ast, qual_type, allow_completion))
return !OT->isIncompleteType();
if (!allow_completion)
return false;
if (class_interface_decl->hasExternalLexicalStorage()) {
if (ast) {
clang::ExternalASTSource *external_ast_source =
ast->getExternalSource();
if (external_ast_source) {
external_ast_source->CompleteType(class_interface_decl);
return !objc_class_type->isIncompleteType();
}
}
}
return false;
}
}
return false;
} break;
case clang::Type::Attributed: