//===--- ClangAdapter.cpp - Interfaces with Clang entities ----------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file provides convenient and canonical interfaces with Clang entities, // serving as both a useful place to put utility functions and a canonical // interface that can abstract nitty gritty Clang internal details. // //===----------------------------------------------------------------------===// #include "CFTypeInfo.h" #include "ClangAdapter.h" #include "ImportName.h" #include "ImporterImpl.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/Lex/Lexer.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" using namespace swift; using namespace importer; /// Get a bit vector indicating which arguments are non-null for a /// given function or method. SmallBitVector importer::getNonNullArgs(const clang::Decl *decl, ArrayRef params) { SmallBitVector result; if (!decl) return result; for (const auto *nonnull : decl->specific_attrs()) { if (!nonnull->args_size()) { // Easy case: all pointer arguments are non-null. if (result.empty()) result.resize(params.size(), true); else result.set(0, params.size()); return result; } // Mark each of the listed parameters as non-null. if (result.empty()) result.resize(params.size(), false); for (auto paramIdx : nonnull->args()) { unsigned idx = paramIdx.getASTIndex(); if (idx < result.size()) result.set(idx); } } return result; } std::optional importer::getDefinitionForClangTypeDecl(const clang::Decl *D) { if (auto OID = dyn_cast(D)) return OID->getDefinition(); if (auto TD = dyn_cast(D)) return TD->getDefinition(); if (auto OPD = dyn_cast(D)) return OPD->getDefinition(); return std::nullopt; } static bool isInLocalScope(const clang::Decl *D) { const clang::DeclContext *LDC = D->getLexicalDeclContext(); while (true) { if (LDC->isFunctionOrMethod()) return true; if (!isa(LDC)) return false; if (const auto *CRD = dyn_cast(LDC)) if (CRD->isLambda()) return true; LDC = LDC->getLexicalParent(); } return false; } const clang::Decl * importer::getFirstNonLocalDecl(const clang::Decl *D) { D = D->getCanonicalDecl(); auto iter = llvm::find_if(D->redecls(), [](const clang::Decl *next) -> bool { return !isInLocalScope(next); }); if (iter == D->redecls_end()) return nullptr; return *iter; } std::optional importer::getClangSubmoduleForDecl(const clang::Decl *D, bool allowForwardDeclaration) { const clang::Decl *actual = nullptr; // Put an Objective-C class into the module that contains the @interface // definition, not just some @class forward declaration. if (auto maybeDefinition = getDefinitionForClangTypeDecl(D)) { actual = maybeDefinition.value(); if (!actual && !allowForwardDeclaration) return std::nullopt; } if (!actual) actual = getFirstNonLocalDecl(D); return actual->getImportedOwningModule(); } /// Retrieve the instance type of the given Clang declaration context. clang::QualType importer::getClangDeclContextType(const clang::DeclContext *dc) { auto &ctx = dc->getParentASTContext(); if (auto objcClass = dyn_cast(dc)) return ctx.getObjCObjectPointerType(ctx.getObjCInterfaceType(objcClass)); if (auto objcCategory = dyn_cast(dc)) { if (objcCategory->isInvalidDecl()) return clang::QualType(); return ctx.getObjCObjectPointerType( ctx.getObjCInterfaceType(objcCategory->getClassInterface())); } if (auto constProto = dyn_cast(dc)) { auto proto = const_cast(constProto); auto type = ctx.getObjCObjectType(ctx.ObjCBuiltinIdTy, {}, {proto}, false); return ctx.getObjCObjectPointerType(type); } if (auto tag = dyn_cast(dc)) { return ctx.getTagDeclType(tag); } return clang::QualType(); } /// Determine whether this is the name of a collection with a single /// element type. static bool isCollectionName(StringRef typeName) { auto lastWord = camel_case::getLastWord(typeName); return lastWord == "Array" || lastWord == "Set"; } /// Retrieve the name of the given Clang type for use when omitting /// needless words. OmissionTypeName importer::getClangTypeNameForOmission(clang::ASTContext &ctx, clang::QualType type) { if (type.isNull()) return OmissionTypeName(); // Dig through the type, looking for a typedef-name and stripping // references along the way. StringRef lastTypedefName; do { // The name of a typedef-name. auto typePtr = type.getTypePtr(); if (auto typedefType = dyn_cast(typePtr)) { auto name = typedefType->getDecl()->getName(); // Objective-C selector type. if (ctx.hasSameUnqualifiedType(type, ctx.getObjCSelType()) && name == "SEL") return "Selector"; // Objective-C "id" type. if (type->isObjCIdType() && name == "id") return "Object"; // Objective-C "Class" type. if (type->isObjCClassType() && name == "Class") return "Class"; // Objective-C "BOOL" type. if (name == "BOOL") return OmissionTypeName("Bool", OmissionTypeFlags::Boolean); // If this is an imported CF type, use that name. StringRef CFName = getCFTypeName(typedefType->getDecl()); if (!CFName.empty()) return CFName; // If we have NS(U)Integer or CGFloat, return it. if (name == "NSInteger" || name == "NSUInteger" || name == "CGFloat") return name; // If it's a collection name and of pointer type, call it an // array of the pointee type. if (isCollectionName(name)) { if (auto ptrType = type->getAs()) { return OmissionTypeName( name, std::nullopt, getClangTypeNameForOmission(ctx, ptrType->getPointeeType()).Name); } } // Otherwise, desugar one level... lastTypedefName = name; type = typedefType->getDecl()->getUnderlyingType(); continue; } // For array types, convert the element type and treat this an as array. if (auto arrayType = dyn_cast(typePtr)) { return OmissionTypeName( "Array", std::nullopt, getClangTypeNameForOmission(ctx, arrayType->getElementType()).Name); } // Look through reference types. if (auto refType = dyn_cast(typePtr)) { type = refType->getPointeeTypeAsWritten(); continue; } // Look through pointer types. if (auto ptrType = dyn_cast(typePtr)) { type = ptrType->getPointeeType(); continue; } // Try to desugar one level... clang::QualType desugared = type.getSingleStepDesugaredType(ctx); if (desugared.getTypePtr() == type.getTypePtr()) break; type = desugared; } while (true); // Objective-C object pointers. if (auto objcObjectPtr = type->getAs()) { auto objcClass = objcObjectPtr->getInterfaceDecl(); // For id or NSObject, retrieve the name of "Proto". if (objcObjectPtr->getNumProtocols() == 1 && (!objcClass || objcClass->getName() == "NSObject")) return (*objcObjectPtr->qual_begin())->getName(); // If there is a class, use it. if (objcClass) { // If this isn't the name of an Objective-C collection, we're done. auto className = objcClass->getName(); if (!isCollectionName(className)) return className; // If we don't have type parameters, use the prefix of the type // name as the collection element type. if (objcClass && !objcClass->getTypeParamList()) { unsigned lastWordSize = camel_case::getLastWord(className).size(); StringRef elementName = className.substr(0, className.size() - lastWordSize); return OmissionTypeName(className, std::nullopt, elementName); } // If we don't have type arguments, the collection element type // is "Object". auto typeArgs = objcObjectPtr->getTypeArgs(); if (typeArgs.empty()) return OmissionTypeName(className, std::nullopt, "Object"); return OmissionTypeName( className, std::nullopt, getClangTypeNameForOmission(ctx, typeArgs[0]).Name); } // Objective-C "id" type. if (objcObjectPtr->isObjCIdType()) return "Object"; // Objective-C "Class" type. if (objcObjectPtr->isObjCClassType()) return "Class"; return StringRef(); } // Handle builtin types by importing them and getting the Swift name. if (auto builtinTy = type->getAs()) { // Names of integer types. static const char *intTypeNames[] = {"UInt8", "UInt16", "UInt32", "UInt64", "UInt128"}; /// Retrieve the name for an integer type based on its size. auto getIntTypeName = [&](bool isSigned) -> StringRef { switch (ctx.getTypeSize(builtinTy)) { case 8: return StringRef(intTypeNames[0]).substr(isSigned ? 1 : 0); case 16: return StringRef(intTypeNames[1]).substr(isSigned ? 1 : 0); case 32: return StringRef(intTypeNames[2]).substr(isSigned ? 1 : 0); case 64: return StringRef(intTypeNames[3]).substr(isSigned ? 1 : 0); case 128: return StringRef(intTypeNames[4]).substr(isSigned ? 1 : 0); default: llvm_unreachable("bad integer type size"); } }; switch (builtinTy->getKind()) { case clang::BuiltinType::Void: return "Void"; case clang::BuiltinType::Bool: return OmissionTypeName("Bool", OmissionTypeFlags::Boolean); case clang::BuiltinType::Float: return "Float"; case clang::BuiltinType::Double: return "Double"; case clang::BuiltinType::Char8: return "UInt8"; case clang::BuiltinType::Char16: return "UInt16"; case clang::BuiltinType::Char32: return "UnicodeScalar"; case clang::BuiltinType::Char_U: case clang::BuiltinType::UChar: case clang::BuiltinType::UShort: case clang::BuiltinType::UInt: case clang::BuiltinType::ULong: case clang::BuiltinType::ULongLong: case clang::BuiltinType::UInt128: case clang::BuiltinType::WChar_U: return getIntTypeName(false); case clang::BuiltinType::Char_S: case clang::BuiltinType::SChar: case clang::BuiltinType::Short: case clang::BuiltinType::Int: case clang::BuiltinType::Long: case clang::BuiltinType::LongLong: case clang::BuiltinType::Int128: case clang::BuiltinType::WChar_S: return getIntTypeName(true); // Types that cannot be mapped into Swift, and probably won't ever be. case clang::BuiltinType::Dependent: case clang::BuiltinType::ARCUnbridgedCast: case clang::BuiltinType::BoundMember: case clang::BuiltinType::BuiltinFn: case clang::BuiltinType::IncompleteMatrixIdx: case clang::BuiltinType::Overload: case clang::BuiltinType::PseudoObject: case clang::BuiltinType::UnknownAny: case clang::BuiltinType::UnresolvedTemplate: return OmissionTypeName(); // FIXME: Types that can be mapped, but aren't yet. case clang::BuiltinType::ShortAccum: case clang::BuiltinType::Accum: case clang::BuiltinType::LongAccum: case clang::BuiltinType::UShortAccum: case clang::BuiltinType::UAccum: case clang::BuiltinType::ULongAccum: case clang::BuiltinType::ShortFract: case clang::BuiltinType::Fract: case clang::BuiltinType::LongFract: case clang::BuiltinType::UShortFract: case clang::BuiltinType::UFract: case clang::BuiltinType::ULongFract: case clang::BuiltinType::SatShortAccum: case clang::BuiltinType::SatAccum: case clang::BuiltinType::SatLongAccum: case clang::BuiltinType::SatUShortAccum: case clang::BuiltinType::SatUAccum: case clang::BuiltinType::SatULongAccum: case clang::BuiltinType::SatShortFract: case clang::BuiltinType::SatFract: case clang::BuiltinType::SatLongFract: case clang::BuiltinType::SatUShortFract: case clang::BuiltinType::SatUFract: case clang::BuiltinType::SatULongFract: case clang::BuiltinType::Half: case clang::BuiltinType::LongDouble: case clang::BuiltinType::BFloat16: case clang::BuiltinType::Float16: case clang::BuiltinType::Float128: case clang::BuiltinType::NullPtr: case clang::BuiltinType::Ibm128: return OmissionTypeName(); // Objective-C types that aren't mapped directly; rather, pointers to // these types will be mapped. case clang::BuiltinType::ObjCClass: case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCSel: return OmissionTypeName(); // OpenMP types that don't have Swift equivalents. case clang::BuiltinType::ArraySection: case clang::BuiltinType::OMPArrayShaping: case clang::BuiltinType::OMPIterator: return OmissionTypeName(); // OpenCL builtin types that don't have Swift equivalents. case clang::BuiltinType::OCLClkEvent: case clang::BuiltinType::OCLEvent: case clang::BuiltinType::OCLSampler: case clang::BuiltinType::OCLQueue: case clang::BuiltinType::OCLReserveID: #define IMAGE_TYPE(Name, Id, ...) case clang::BuiltinType::Id: #include "clang/Basic/OpenCLImageTypes.def" #define EXT_OPAQUE_TYPE(Name, Id, ...) case clang::BuiltinType::Id: #include "clang/Basic/OpenCLExtensionTypes.def" return OmissionTypeName(); // ARM SVE builtin types that don't have Swift equivalents. #define SVE_TYPE(Name, Id, ...) case clang::BuiltinType::Id: #include "clang/Basic/AArch64SVEACLETypes.def" return OmissionTypeName(); // PPC MMA builtin types that don't have Swift equivalents. #define PPC_VECTOR_TYPE(Name, Id, ...) case clang::BuiltinType::Id: #include "clang/Basic/PPCTypes.def" return OmissionTypeName(); // RISC-V V builtin types that don't have Swift equivalents. #define RVV_TYPE(Name, Id, ...) case clang::BuiltinType::Id: #include "clang/Basic/RISCVVTypes.def" return OmissionTypeName(); // WASM builtin types that don't have Swift equivalents. #define WASM_TYPE(Name, Id, ...) case clang::BuiltinType::Id: #include "clang/Basic/WebAssemblyReferenceTypes.def" return OmissionTypeName(); // AMDGPU builtins that don't have Swift equivalents. #define AMDGPU_TYPE(Name, Id, ...) case clang::BuiltinType::Id: #include "clang/Basic/AMDGPUTypes.def" return OmissionTypeName(); } } // Tag types. if (auto tagType = type->getAs()) { if (tagType->getDecl()->getName().empty()) return lastTypedefName; return tagType->getDecl()->getName(); } // Block pointers. if (type->getAs()) return OmissionTypeName("Block", OmissionTypeFlags::Function); // Function pointers. if (type->isFunctionType()) return OmissionTypeName("Function", OmissionTypeFlags::Function); return StringRef(); } static clang::SwiftNewTypeAttr * retrieveNewTypeAttr(const clang::TypedefNameDecl *decl) { // Retrieve the attribute. auto attr = decl->getAttr(); if (!attr) return nullptr; // FIXME: CFErrorDomain is marked as CF_EXTENSIBLE_STRING_ENUM, but it turned // out to be more disruptive than not to leave it that way. auto name = decl->getName(); if (name == "CFErrorDomain") return nullptr; return attr; } clang::SwiftNewTypeAttr * importer::getSwiftNewtypeAttr(const clang::TypedefNameDecl *decl, ImportNameVersion version) { // Newtype was introduced in Swift 3 if (version <= ImportNameVersion::swift2()) return nullptr; return retrieveNewTypeAttr(decl); } // If this decl is associated with a swift_newtype typedef, return it, otherwise // null clang::TypedefNameDecl *importer::findSwiftNewtype(const clang::NamedDecl *decl, clang::Sema &clangSema, ImportNameVersion version) { // Newtype was introduced in Swift 3 if (version <= ImportNameVersion::swift2()) return nullptr; auto varDecl = dyn_cast(decl); if (!varDecl) return nullptr; if (auto typedefTy = varDecl->getType()->getAs()) if (retrieveNewTypeAttr(typedefTy->getDecl())) return typedefTy->getDecl(); // Special case: "extern NSString * fooNotification" adopts // NSNotificationName type, and is a member of NSNotificationName if (isNSNotificationGlobal(decl)) { clang::IdentifierInfo *notificationName = &clangSema.getASTContext().Idents.get("NSNotificationName"); clang::LookupResult lookupResult(clangSema, notificationName, clang::SourceLocation(), clang::Sema::LookupOrdinaryName); if (!clangSema.LookupQualifiedName( lookupResult, /*LookupCtx*/ clangSema.getASTContext().getTranslationUnitDecl())) return nullptr; auto nsDecl = lookupResult.getAsSingle(); if (!nsDecl) return nullptr; // Make sure it also has a newtype decl on it if (retrieveNewTypeAttr(nsDecl)) return nsDecl; return nullptr; } return nullptr; } bool importer::isNSString(const clang::Type *type) { if (auto ptrType = type->getAs()) if (auto interfaceType = ptrType->getInterfaceType()) if (interfaceType->getDecl()->getName() == "NSString") return true; return false; } bool importer::isNSString(clang::QualType qt) { return qt.getTypePtrOrNull() && isNSString(qt.getTypePtrOrNull()); } bool importer::isNSNotificationName(clang::QualType type) { if (auto *typealias = type->getAs()) { return typealias->getDecl()->getName() == "NSNotificationName"; } return false; } bool importer::isNSNotificationGlobal(const clang::NamedDecl *decl) { // Looking for: extern NSString *fooNotification; // Must be extern global variable auto vDecl = dyn_cast(decl); if (!vDecl || !vDecl->hasExternalFormalLinkage()) return false; // No explicit swift_name if (decl->getAttr()) return false; // Must end in Notification if (!vDecl->getDeclName().isIdentifier()) return false; if (stripNotification(vDecl->getName()).empty()) return false; // Must be NSString * if (!isNSString(vDecl->getType())) return false; // We're a match! return true; } bool importer::hasNativeSwiftDecl(const clang::Decl *decl) { if (auto *attr = decl->getAttr()) if (attr->getGeneratedDeclaration() && attr->getLanguage() == "Swift") return true; return false; } /// Translate the "nullability" notion from API notes into an optional type /// kind. OptionalTypeKind importer::translateNullability( clang::NullabilityKind kind, bool stripNonResultOptionality) { if (stripNonResultOptionality && kind != clang::NullabilityKind::NullableResult) return OptionalTypeKind::OTK_None; switch (kind) { case clang::NullabilityKind::NonNull: return OptionalTypeKind::OTK_None; case clang::NullabilityKind::Nullable: case clang::NullabilityKind::NullableResult: return OptionalTypeKind::OTK_Optional; case clang::NullabilityKind::Unspecified: return OptionalTypeKind::OTK_ImplicitlyUnwrappedOptional; } llvm_unreachable("Invalid NullabilityKind."); return OptionalTypeKind::OTK_Optional; } bool importer::isRequiredInitializer(const clang::ObjCMethodDecl *method) { // FIXME: No way to express this in Objective-C. return false; } /// Check if this method is declared in the context that conforms to /// NSAccessibility. static bool isAccessibilityConformingContext(const clang::DeclContext *ctx) { const clang::ObjCProtocolList *protocols = nullptr; if (auto protocol = dyn_cast(ctx)) { if (protocol->getName() == "NSAccessibility") return true; return false; } else if (auto interface = dyn_cast(ctx)) protocols = &interface->getReferencedProtocols(); else if (auto category = dyn_cast(ctx)) protocols = &category->getReferencedProtocols(); else return false; for (auto pi : *protocols) { if (pi->getName() == "NSAccessibility") return true; } return false; } bool importer::shouldImportPropertyAsAccessors(const clang::ObjCPropertyDecl *prop) { if (prop->hasAttr()) return true; // Check if the property is one of the specially handled accessibility APIs. // // These appear as both properties and methods in ObjC and should be // imported as methods into Swift, as a sort of least-common-denominator // compromise. if (!prop->getName().starts_with("accessibility")) return false; if (isAccessibilityConformingContext(prop->getDeclContext())) return true; return false; } bool importer::isInitMethod(const clang::ObjCMethodDecl *method) { // init methods are always instance methods. if (!method->isInstanceMethod()) return false; // init methods must be classified as such by Clang. if (method->getMethodFamily() != clang::OMF_init) return false; // Swift restriction: init methods must start with the word "init". auto selector = method->getSelector(); return camel_case::getFirstWord(selector.getNameForSlot(0)) == "init"; } bool importer::isObjCId(const clang::Decl *decl) { auto typedefDecl = dyn_cast(decl); if (!typedefDecl) return false; if (!typedefDecl->getDeclContext()->getRedeclContext()->isTranslationUnit()) return false; return typedefDecl->getName() == "id"; } bool importer::isUnavailableInSwift( const clang::Decl *decl, const PlatformAvailability *platformAvailability, bool enableObjCInterop) { // 'id' is always unavailable in Swift. if (enableObjCInterop && isObjCId(decl)) return true; if (decl->isUnavailable()) return true; for (auto *attr : decl->specific_attrs()) { if (attr->getPlatform()->getName() == "swift") return true; if (!platformAvailability) continue; if (!platformAvailability->isPlatformRelevant( attr->getPlatform()->getName())) { continue; } llvm::VersionTuple version = attr->getDeprecated(); if (version.empty()) continue; if (platformAvailability->treatDeprecatedAsUnavailable( decl, version, /*isAsync=*/false)) { return true; } } return false; } OptionalTypeKind importer::getParamOptionality(const clang::ParmVarDecl *param, bool knownNonNull) { // If nullability is available on the type, use it. clang::QualType paramTy = param->getType(); if (auto nullability = paramTy->getNullability()) { return translateNullability(*nullability); } // If it's known non-null, use that. if (knownNonNull || param->hasAttr()) return OTK_None; // Check for the 'static' annotation on C arrays. if (const auto *DT = dyn_cast(paramTy)) if (const auto *AT = DT->getOriginalType()->getAsArrayTypeUnsafe()) if (AT->getSizeModifier() == clang::ArraySizeModifier::Static) return OTK_None; // Default to implicitly unwrapped optionals. return OTK_ImplicitlyUnwrappedOptional; }