//===- CXTypes.cpp - Implements 'CXTypes' aspect of libclang ------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===--------------------------------------------------------------------===// // // This file implements the 'CXTypes' API hooks in the Clang-C library. // //===--------------------------------------------------------------------===// #include "CIndexer.h" #include "CXCursor.h" #include "CXString.h" #include "CXTranslationUnit.h" #include "CXType.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" #include "clang/Frontend/ASTUnit.h" using namespace clang; static CXTypeKind GetBuiltinTypeKind(const BuiltinType *BT) { #define BTCASE(K) case BuiltinType::K: return CXType_##K switch (BT->getKind()) { BTCASE(Void); BTCASE(Bool); BTCASE(Char_U); BTCASE(UChar); BTCASE(Char16); BTCASE(Char32); BTCASE(UShort); BTCASE(UInt); BTCASE(ULong); BTCASE(ULongLong); BTCASE(UInt128); BTCASE(Char_S); BTCASE(SChar); case BuiltinType::WChar_S: return CXType_WChar; case BuiltinType::WChar_U: return CXType_WChar; BTCASE(Short); BTCASE(Int); BTCASE(Long); BTCASE(LongLong); BTCASE(Int128); BTCASE(Float); BTCASE(Double); BTCASE(LongDouble); BTCASE(NullPtr); BTCASE(Overload); BTCASE(Dependent); BTCASE(ObjCId); BTCASE(ObjCClass); BTCASE(ObjCSel); default: return CXType_Unexposed; } #undef BTCASE } static CXTypeKind GetTypeKind(QualType T) { const Type *TP = T.getTypePtrOrNull(); if (!TP) return CXType_Invalid; #define TKCASE(K) case Type::K: return CXType_##K switch (TP->getTypeClass()) { case Type::Builtin: return GetBuiltinTypeKind(cast(TP)); TKCASE(Complex); TKCASE(Pointer); TKCASE(BlockPointer); TKCASE(LValueReference); TKCASE(RValueReference); TKCASE(Record); TKCASE(Enum); TKCASE(Typedef); TKCASE(ObjCInterface); TKCASE(ObjCObjectPointer); TKCASE(FunctionNoProto); TKCASE(FunctionProto); TKCASE(ConstantArray); TKCASE(Vector); default: return CXType_Unexposed; } #undef TKCASE } CXType cxtype::MakeCXType(QualType T, CXTranslationUnit TU) { CXTypeKind TK = CXType_Invalid; if (TU && !T.isNull()) { ASTContext &Ctx = cxtu::getASTUnit(TU)->getASTContext(); if (Ctx.getLangOpts().ObjC1) { QualType UnqualT = T.getUnqualifiedType(); if (Ctx.isObjCIdType(UnqualT)) TK = CXType_ObjCId; else if (Ctx.isObjCClassType(UnqualT)) TK = CXType_ObjCClass; else if (Ctx.isObjCSelType(UnqualT)) TK = CXType_ObjCSel; } } if (TK == CXType_Invalid) TK = GetTypeKind(T); CXType CT = { TK, { TK == CXType_Invalid ? 0 : T.getAsOpaquePtr(), TU }}; return CT; } using cxtype::MakeCXType; static inline QualType GetQualType(CXType CT) { return QualType::getFromOpaquePtr(CT.data[0]); } static inline CXTranslationUnit GetTU(CXType CT) { return static_cast(CT.data[1]); } extern "C" { CXType clang_getCursorType(CXCursor C) { using namespace cxcursor; CXTranslationUnit TU = cxcursor::getCursorTU(C); if (!TU) return MakeCXType(QualType(), TU); ASTContext &Context = cxtu::getASTUnit(TU)->getASTContext(); if (clang_isExpression(C.kind)) { QualType T = cxcursor::getCursorExpr(C)->getType(); return MakeCXType(T, TU); } if (clang_isDeclaration(C.kind)) { const Decl *D = cxcursor::getCursorDecl(C); if (!D) return MakeCXType(QualType(), TU); if (const TypeDecl *TD = dyn_cast(D)) return MakeCXType(Context.getTypeDeclType(TD), TU); if (const ObjCInterfaceDecl *ID = dyn_cast(D)) return MakeCXType(Context.getObjCInterfaceType(ID), TU); if (const DeclaratorDecl *DD = dyn_cast(D)) { if (TypeSourceInfo *TSInfo = DD->getTypeSourceInfo()) return MakeCXType(TSInfo->getType(), TU); return MakeCXType(DD->getType(), TU); } if (const ValueDecl *VD = dyn_cast(D)) return MakeCXType(VD->getType(), TU); if (const ObjCPropertyDecl *PD = dyn_cast(D)) return MakeCXType(PD->getType(), TU); if (const FunctionTemplateDecl *FTD = dyn_cast(D)) { if (TypeSourceInfo *TSInfo = FTD->getTemplatedDecl()->getTypeSourceInfo()) return MakeCXType(TSInfo->getType(), TU); return MakeCXType(FTD->getTemplatedDecl()->getType(), TU); } return MakeCXType(QualType(), TU); } if (clang_isReference(C.kind)) { switch (C.kind) { case CXCursor_ObjCSuperClassRef: { QualType T = Context.getObjCInterfaceType(getCursorObjCSuperClassRef(C).first); return MakeCXType(T, TU); } case CXCursor_ObjCClassRef: { QualType T = Context.getObjCInterfaceType(getCursorObjCClassRef(C).first); return MakeCXType(T, TU); } case CXCursor_TypeRef: { QualType T = Context.getTypeDeclType(getCursorTypeRef(C).first); return MakeCXType(T, TU); } case CXCursor_CXXBaseSpecifier: return cxtype::MakeCXType(getCursorCXXBaseSpecifier(C)->getType(), TU); case CXCursor_MemberRef: return cxtype::MakeCXType(getCursorMemberRef(C).first->getType(), TU); case CXCursor_VariableRef: return cxtype::MakeCXType(getCursorVariableRef(C).first->getType(), TU); case CXCursor_ObjCProtocolRef: case CXCursor_TemplateRef: case CXCursor_NamespaceRef: case CXCursor_OverloadedDeclRef: default: break; } return MakeCXType(QualType(), TU); } return MakeCXType(QualType(), TU); } CXString clang_getTypeSpelling(CXType CT) { QualType T = GetQualType(CT); if (T.isNull()) return cxstring::createEmpty(); CXTranslationUnit TU = GetTU(CT); SmallString<64> Str; llvm::raw_svector_ostream OS(Str); PrintingPolicy PP(cxtu::getASTUnit(TU)->getASTContext().getLangOpts()); T.print(OS, PP); return cxstring::createDup(OS.str()); } CXType clang_getTypedefDeclUnderlyingType(CXCursor C) { using namespace cxcursor; CXTranslationUnit TU = cxcursor::getCursorTU(C); if (clang_isDeclaration(C.kind)) { const Decl *D = cxcursor::getCursorDecl(C); if (const TypedefNameDecl *TD = dyn_cast_or_null(D)) { QualType T = TD->getUnderlyingType(); return MakeCXType(T, TU); } return MakeCXType(QualType(), TU); } return MakeCXType(QualType(), TU); } CXType clang_getEnumDeclIntegerType(CXCursor C) { using namespace cxcursor; CXTranslationUnit TU = cxcursor::getCursorTU(C); if (clang_isDeclaration(C.kind)) { const Decl *D = cxcursor::getCursorDecl(C); if (const EnumDecl *TD = dyn_cast_or_null(D)) { QualType T = TD->getIntegerType(); return MakeCXType(T, TU); } return MakeCXType(QualType(), TU); } return MakeCXType(QualType(), TU); } long long clang_getEnumConstantDeclValue(CXCursor C) { using namespace cxcursor; if (clang_isDeclaration(C.kind)) { const Decl *D = cxcursor::getCursorDecl(C); if (const EnumConstantDecl *TD = dyn_cast_or_null(D)) { return TD->getInitVal().getSExtValue(); } return LLONG_MIN; } return LLONG_MIN; } unsigned long long clang_getEnumConstantDeclUnsignedValue(CXCursor C) { using namespace cxcursor; if (clang_isDeclaration(C.kind)) { const Decl *D = cxcursor::getCursorDecl(C); if (const EnumConstantDecl *TD = dyn_cast_or_null(D)) { return TD->getInitVal().getZExtValue(); } return ULLONG_MAX; } return ULLONG_MAX; } int clang_getFieldDeclBitWidth(CXCursor C) { using namespace cxcursor; if (clang_isDeclaration(C.kind)) { const Decl *D = getCursorDecl(C); if (const FieldDecl *FD = dyn_cast_or_null(D)) { if (FD->isBitField()) return FD->getBitWidthValue(getCursorContext(C)); } } return -1; } CXType clang_getCanonicalType(CXType CT) { if (CT.kind == CXType_Invalid) return CT; QualType T = GetQualType(CT); CXTranslationUnit TU = GetTU(CT); if (T.isNull()) return MakeCXType(QualType(), GetTU(CT)); return MakeCXType(cxtu::getASTUnit(TU)->getASTContext() .getCanonicalType(T), TU); } unsigned clang_isConstQualifiedType(CXType CT) { QualType T = GetQualType(CT); return T.isLocalConstQualified(); } unsigned clang_isVolatileQualifiedType(CXType CT) { QualType T = GetQualType(CT); return T.isLocalVolatileQualified(); } unsigned clang_isRestrictQualifiedType(CXType CT) { QualType T = GetQualType(CT); return T.isLocalRestrictQualified(); } CXType clang_getPointeeType(CXType CT) { QualType T = GetQualType(CT); const Type *TP = T.getTypePtrOrNull(); if (!TP) return MakeCXType(QualType(), GetTU(CT)); switch (TP->getTypeClass()) { case Type::Pointer: T = cast(TP)->getPointeeType(); break; case Type::BlockPointer: T = cast(TP)->getPointeeType(); break; case Type::LValueReference: case Type::RValueReference: T = cast(TP)->getPointeeType(); break; case Type::ObjCObjectPointer: T = cast(TP)->getPointeeType(); break; default: T = QualType(); break; } return MakeCXType(T, GetTU(CT)); } CXCursor clang_getTypeDeclaration(CXType CT) { if (CT.kind == CXType_Invalid) return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); QualType T = GetQualType(CT); const Type *TP = T.getTypePtrOrNull(); if (!TP) return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); Decl *D = 0; try_again: switch (TP->getTypeClass()) { case Type::Typedef: D = cast(TP)->getDecl(); break; case Type::ObjCObject: D = cast(TP)->getInterface(); break; case Type::ObjCInterface: D = cast(TP)->getDecl(); break; case Type::Record: case Type::Enum: D = cast(TP)->getDecl(); break; case Type::TemplateSpecialization: if (const RecordType *Record = TP->getAs()) D = Record->getDecl(); else D = cast(TP)->getTemplateName() .getAsTemplateDecl(); break; case Type::InjectedClassName: D = cast(TP)->getDecl(); break; // FIXME: Template type parameters! case Type::Elaborated: TP = cast(TP)->getNamedType().getTypePtrOrNull(); goto try_again; default: break; } if (!D) return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); return cxcursor::MakeCXCursor(D, GetTU(CT)); } CXString clang_getTypeKindSpelling(enum CXTypeKind K) { const char *s = 0; #define TKIND(X) case CXType_##X: s = "" #X ""; break switch (K) { TKIND(Invalid); TKIND(Unexposed); TKIND(Void); TKIND(Bool); TKIND(Char_U); TKIND(UChar); TKIND(Char16); TKIND(Char32); TKIND(UShort); TKIND(UInt); TKIND(ULong); TKIND(ULongLong); TKIND(UInt128); TKIND(Char_S); TKIND(SChar); case CXType_WChar: s = "WChar"; break; TKIND(Short); TKIND(Int); TKIND(Long); TKIND(LongLong); TKIND(Int128); TKIND(Float); TKIND(Double); TKIND(LongDouble); TKIND(NullPtr); TKIND(Overload); TKIND(Dependent); TKIND(ObjCId); TKIND(ObjCClass); TKIND(ObjCSel); TKIND(Complex); TKIND(Pointer); TKIND(BlockPointer); TKIND(LValueReference); TKIND(RValueReference); TKIND(Record); TKIND(Enum); TKIND(Typedef); TKIND(ObjCInterface); TKIND(ObjCObjectPointer); TKIND(FunctionNoProto); TKIND(FunctionProto); TKIND(ConstantArray); TKIND(Vector); } #undef TKIND return cxstring::createRef(s); } unsigned clang_equalTypes(CXType A, CXType B) { return A.data[0] == B.data[0] && A.data[1] == B.data[1];; } unsigned clang_isFunctionTypeVariadic(CXType X) { QualType T = GetQualType(X); if (T.isNull()) return 0; if (const FunctionProtoType *FD = T->getAs()) return (unsigned)FD->isVariadic(); if (T->getAs()) return 1; return 0; } CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { QualType T = GetQualType(X); if (T.isNull()) return CXCallingConv_Invalid; if (const FunctionType *FD = T->getAs()) { #define TCALLINGCONV(X) case CC_##X: return CXCallingConv_##X switch (FD->getCallConv()) { TCALLINGCONV(Default); TCALLINGCONV(C); TCALLINGCONV(X86StdCall); TCALLINGCONV(X86FastCall); TCALLINGCONV(X86ThisCall); TCALLINGCONV(X86Pascal); TCALLINGCONV(AAPCS); TCALLINGCONV(AAPCS_VFP); TCALLINGCONV(PnaclCall); TCALLINGCONV(IntelOclBicc); } #undef TCALLINGCONV } return CXCallingConv_Invalid; } int clang_getNumArgTypes(CXType X) { QualType T = GetQualType(X); if (T.isNull()) return -1; if (const FunctionProtoType *FD = T->getAs()) { return FD->getNumArgs(); } if (T->getAs()) { return 0; } return -1; } CXType clang_getArgType(CXType X, unsigned i) { QualType T = GetQualType(X); if (T.isNull()) return MakeCXType(QualType(), GetTU(X)); if (const FunctionProtoType *FD = T->getAs()) { unsigned numArgs = FD->getNumArgs(); if (i >= numArgs) return MakeCXType(QualType(), GetTU(X)); return MakeCXType(FD->getArgType(i), GetTU(X)); } return MakeCXType(QualType(), GetTU(X)); } CXType clang_getResultType(CXType X) { QualType T = GetQualType(X); if (T.isNull()) return MakeCXType(QualType(), GetTU(X)); if (const FunctionType *FD = T->getAs()) return MakeCXType(FD->getResultType(), GetTU(X)); return MakeCXType(QualType(), GetTU(X)); } CXType clang_getCursorResultType(CXCursor C) { if (clang_isDeclaration(C.kind)) { const Decl *D = cxcursor::getCursorDecl(C); if (const ObjCMethodDecl *MD = dyn_cast_or_null(D)) return MakeCXType(MD->getResultType(), cxcursor::getCursorTU(C)); return clang_getResultType(clang_getCursorType(C)); } return MakeCXType(QualType(), cxcursor::getCursorTU(C)); } unsigned clang_isPODType(CXType X) { QualType T = GetQualType(X); if (T.isNull()) return 0; CXTranslationUnit TU = GetTU(X); return T.isPODType(cxtu::getASTUnit(TU)->getASTContext()) ? 1 : 0; } CXType clang_getElementType(CXType CT) { QualType ET = QualType(); QualType T = GetQualType(CT); const Type *TP = T.getTypePtrOrNull(); if (TP) { switch (TP->getTypeClass()) { case Type::ConstantArray: ET = cast (TP)->getElementType(); break; case Type::Vector: ET = cast (TP)->getElementType(); break; case Type::Complex: ET = cast (TP)->getElementType(); break; default: break; } } return MakeCXType(ET, GetTU(CT)); } long long clang_getNumElements(CXType CT) { long long result = -1; QualType T = GetQualType(CT); const Type *TP = T.getTypePtrOrNull(); if (TP) { switch (TP->getTypeClass()) { case Type::ConstantArray: result = cast (TP)->getSize().getSExtValue(); break; case Type::Vector: result = cast (TP)->getNumElements(); break; default: break; } } return result; } CXType clang_getArrayElementType(CXType CT) { QualType ET = QualType(); QualType T = GetQualType(CT); const Type *TP = T.getTypePtrOrNull(); if (TP) { switch (TP->getTypeClass()) { case Type::ConstantArray: ET = cast (TP)->getElementType(); break; default: break; } } return MakeCXType(ET, GetTU(CT)); } long long clang_getArraySize(CXType CT) { long long result = -1; QualType T = GetQualType(CT); const Type *TP = T.getTypePtrOrNull(); if (TP) { switch (TP->getTypeClass()) { case Type::ConstantArray: result = cast (TP)->getSize().getSExtValue(); break; default: break; } } 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()) { 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(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(Res.front())) return Ctx.getFieldOffset(FD); if (const IndirectFieldDecl *IFD = dyn_cast(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(cxcursor::getCursorDecl(C)); if (!FD) return 0; return FD->isBitField(); } CXString clang_getDeclObjCTypeEncoding(CXCursor C) { if (!clang_isDeclaration(C.kind)) return cxstring::createEmpty(); const Decl *D = cxcursor::getCursorDecl(C); ASTContext &Ctx = cxcursor::getCursorContext(C); std::string encoding; if (const ObjCMethodDecl *OMD = dyn_cast(D)) { if (Ctx.getObjCEncodingForMethodDecl(OMD, encoding)) return cxstring::createRef("?"); } else if (const ObjCPropertyDecl *OPD = dyn_cast(D)) Ctx.getObjCEncodingForPropertyDecl(OPD, NULL, encoding); else if (const FunctionDecl *FD = dyn_cast(D)) Ctx.getObjCEncodingForFunctionDecl(FD, encoding); else { QualType Ty; if (const TypeDecl *TD = dyn_cast(D)) Ty = Ctx.getTypeDeclType(TD); if (const ValueDecl *VD = dyn_cast(D)) Ty = VD->getType(); else return cxstring::createRef("?"); Ctx.getObjCEncodingForType(Ty, encoding); } return cxstring::createDup(encoding); } } // end: extern "C"