| //===--- SemaAPINotes.cpp - API Notes Handling ----------------------------===// |
| // |
| // 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 mapping from API notes to declaration attributes. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/Sema/SemaInternal.h" |
| #include "clang/AST/DeclObjC.h" |
| #include "clang/APINotes/APINotesReader.h" |
| using namespace clang; |
| |
| namespace { |
| enum IsActive_t : bool { |
| IsNotActive, |
| IsActive |
| }; |
| |
| struct VersionedInfoMetadata { |
| /// An empty version refers to unversioned metadata. |
| VersionTuple Version; |
| bool IsActive; |
| |
| VersionedInfoMetadata(VersionTuple version, IsActive_t active) |
| : Version(version), IsActive(active == IsActive_t::IsActive) {} |
| }; |
| } // end anonymous namespace |
| |
| /// Determine whether this is a multi-level pointer type. |
| static bool isMultiLevelPointerType(QualType type) { |
| QualType pointee = type->getPointeeType(); |
| if (pointee.isNull()) |
| return false; |
| |
| return pointee->isAnyPointerType() || pointee->isObjCObjectPointerType() || |
| pointee->isMemberPointerType(); |
| } |
| |
| // Apply nullability to the given declaration. |
| static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability, |
| VersionedInfoMetadata metadata) { |
| if (!metadata.IsActive) |
| return; |
| |
| QualType type; |
| |
| // Nullability for a function/method appertains to the retain type. |
| if (auto function = dyn_cast<FunctionDecl>(decl)) { |
| type = function->getReturnType(); |
| } else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) { |
| type = method->getReturnType(); |
| } else if (auto value = dyn_cast<ValueDecl>(decl)) { |
| type = value->getType(); |
| } else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) { |
| type = property->getType(); |
| } else { |
| return; |
| } |
| |
| // Check the nullability specifier on this type. |
| QualType origType = type; |
| S.checkNullabilityTypeSpecifier(type, nullability, decl->getLocation(), |
| /*isContextSensitive=*/false, |
| isa<ParmVarDecl>(decl), /*implicit=*/true, |
| /*overrideExisting=*/true); |
| if (type.getTypePtr() == origType.getTypePtr()) |
| return; |
| |
| if (auto function = dyn_cast<FunctionDecl>(decl)) { |
| const FunctionType *fnType = function->getType()->castAs<FunctionType>(); |
| if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(fnType)) { |
| function->setType(S.Context.getFunctionType(type, proto->getParamTypes(), |
| proto->getExtProtoInfo())); |
| } else { |
| function->setType(S.Context.getFunctionNoProtoType(type, |
| fnType->getExtInfo())); |
| } |
| } else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) { |
| method->setReturnType(type); |
| |
| // Make it a context-sensitive keyword if we can. |
| if (!isMultiLevelPointerType(type)) { |
| method->setObjCDeclQualifier( |
| Decl::ObjCDeclQualifier(method->getObjCDeclQualifier() | |
| Decl::OBJC_TQ_CSNullability)); |
| } |
| } else if (auto value = dyn_cast<ValueDecl>(decl)) { |
| value->setType(type); |
| |
| // Make it a context-sensitive keyword if we can. |
| if (auto parm = dyn_cast<ParmVarDecl>(decl)) { |
| if (parm->isObjCMethodParameter() && !isMultiLevelPointerType(type)) { |
| parm->setObjCDeclQualifier( |
| Decl::ObjCDeclQualifier(parm->getObjCDeclQualifier() | |
| Decl::OBJC_TQ_CSNullability)); |
| } |
| } |
| } else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) { |
| property->setType(type, property->getTypeSourceInfo()); |
| |
| // Make it a property attribute if we can. |
| if (!isMultiLevelPointerType(type)) { |
| property->setPropertyAttributes( |
| ObjCPropertyDecl::OBJC_PR_null_resettable); |
| } |
| } else { |
| llvm_unreachable("cannot handle nullability here"); |
| } |
| } |
| |
| /// Copy a string into ASTContext-allocated memory. |
| static StringRef CopyString(ASTContext &ctx, StringRef string) { |
| void *mem = ctx.Allocate(string.size(), alignof(char)); |
| memcpy(mem, string.data(), string.size()); |
| return StringRef(static_cast<char *>(mem), string.size()); |
| } |
| |
| namespace { |
| template <typename A> |
| struct AttrKindFor {}; |
| |
| #define ATTR(X) \ |
| template <> struct AttrKindFor<X##Attr> { \ |
| static const attr::Kind value = attr::X; \ |
| }; |
| #include "clang/Basic/AttrList.inc" |
| |
| /// Handle an attribute introduced by API notes. |
| /// |
| /// \param shouldAddAttribute Whether we should add a new attribute |
| /// (otherwise, we might remove an existing attribute). |
| /// \param createAttr Create the new attribute to be added. |
| template<typename A> |
| void handleAPINotedAttribute( |
| Sema &S, Decl *D, bool shouldAddAttribute, |
| VersionedInfoMetadata metadata, |
| llvm::function_ref<A *()> createAttr, |
| llvm::function_ref<specific_attr_iterator<A>(Decl*)> getExistingAttr) { |
| if (metadata.IsActive) { |
| auto end = D->specific_attr_end<A>(); |
| auto existing = getExistingAttr(D); |
| if (existing != end) { |
| // Remove the existing attribute, and treat it as a superseded |
| // non-versioned attribute. |
| auto *versioned = |
| SwiftVersionedAttr::CreateImplicit(S.Context, clang::VersionTuple(), |
| *existing); |
| |
| D->getAttrs().erase(existing.getCurrent()); |
| D->addAttr(versioned); |
| } |
| |
| // If we're supposed to add a new attribute, do so. |
| if (shouldAddAttribute) { |
| if (auto attr = createAttr()) { |
| D->addAttr(attr); |
| } |
| } |
| |
| } else { |
| if (shouldAddAttribute) { |
| if (auto attr = createAttr()) { |
| auto *versioned = |
| SwiftVersionedAttr::CreateImplicit(S.Context, metadata.Version, |
| attr); |
| D->addAttr(versioned); |
| } |
| } else { |
| // FIXME: This isn't preserving enough information for things like |
| // availability, where we're trying to remove a /specific/ kind of |
| // attribute. |
| auto *versioned = |
| SwiftVersionedRemovalAttr::CreateImplicit(S.Context, |
| metadata.Version, |
| AttrKindFor<A>::value); |
| D->addAttr(versioned); |
| } |
| } |
| } |
| |
| template<typename A> |
| void handleAPINotedAttribute( |
| Sema &S, Decl *D, bool shouldAddAttribute, |
| VersionedInfoMetadata metadata, |
| llvm::function_ref<A *()> createAttr) { |
| handleAPINotedAttribute<A>(S, D, shouldAddAttribute, metadata, createAttr, |
| [](Decl *decl) { |
| return decl->specific_attr_begin<A>(); |
| }); |
| } |
| } |
| |
| static void ProcessAPINotes(Sema &S, Decl *D, |
| const api_notes::CommonEntityInfo &info, |
| VersionedInfoMetadata metadata) { |
| // Availability |
| if (info.Unavailable) { |
| handleAPINotedAttribute<UnavailableAttr>(S, D, true, metadata, |
| [&] { |
| return UnavailableAttr::CreateImplicit(S.Context, |
| CopyString(S.Context, |
| info.UnavailableMsg)); |
| }); |
| } |
| |
| if (info.UnavailableInSwift) { |
| handleAPINotedAttribute<AvailabilityAttr>(S, D, true, metadata, [&] { |
| return AvailabilityAttr::CreateImplicit( |
| S.Context, |
| &S.Context.Idents.get("swift"), |
| VersionTuple(), |
| VersionTuple(), |
| VersionTuple(), |
| /*Unavailable=*/true, |
| CopyString(S.Context, info.UnavailableMsg), |
| /*Strict=*/false, |
| /*Replacement=*/StringRef()); |
| }, |
| [](Decl *decl) { |
| auto existing = decl->specific_attr_begin<AvailabilityAttr>(), |
| end = decl->specific_attr_end<AvailabilityAttr>(); |
| while (existing != end) { |
| if (auto platform = (*existing)->getPlatform()) { |
| if (platform->isStr("swift")) |
| break; |
| } |
| |
| ++existing; |
| } |
| |
| return existing; |
| }); |
| } |
| |
| // swift_private |
| if (auto swiftPrivate = info.isSwiftPrivate()) { |
| handleAPINotedAttribute<SwiftPrivateAttr>(S, D, *swiftPrivate, metadata, |
| [&] { |
| return SwiftPrivateAttr::CreateImplicit(S.Context); |
| }); |
| } |
| |
| // swift_name |
| if (!info.SwiftName.empty()) { |
| handleAPINotedAttribute<SwiftNameAttr>(S, D, true, metadata, |
| [&]() -> SwiftNameAttr * { |
| auto &APINoteName = S.getASTContext().Idents.get("SwiftName API Note"); |
| |
| if (!S.DiagnoseSwiftName(D, info.SwiftName, D->getLocation(), |
| &APINoteName)) { |
| return nullptr; |
| } |
| |
| return SwiftNameAttr::CreateImplicit(S.Context, |
| CopyString(S.Context, |
| info.SwiftName)); |
| }); |
| } |
| } |
| |
| static void ProcessAPINotes(Sema &S, Decl *D, |
| const api_notes::CommonTypeInfo &info, |
| VersionedInfoMetadata metadata) { |
| // swift_bridge |
| if (auto swiftBridge = info.getSwiftBridge()) { |
| handleAPINotedAttribute<SwiftBridgeAttr>(S, D, !swiftBridge->empty(), |
| metadata, [&] { |
| return SwiftBridgeAttr::CreateImplicit(S.Context, |
| CopyString(S.Context, |
| *swiftBridge)); |
| }); |
| } |
| |
| // ns_error_domain |
| if (auto nsErrorDomain = info.getNSErrorDomain()) { |
| handleAPINotedAttribute<NSErrorDomainAttr>(S, D, !nsErrorDomain->empty(), |
| metadata, [&] { |
| return NSErrorDomainAttr::CreateImplicit( |
| S.Context, |
| &S.Context.Idents.get(*nsErrorDomain)); |
| }); |
| } |
| |
| ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), |
| metadata); |
| } |
| |
| /// Check that the replacement type provided by API notes is reasonable. |
| /// |
| /// This is a very weak form of ABI check. |
| static bool checkAPINotesReplacementType(Sema &S, SourceLocation loc, |
| QualType origType, |
| QualType replacementType) { |
| if (S.Context.getTypeSize(origType) != |
| S.Context.getTypeSize(replacementType)) { |
| S.Diag(loc, diag::err_incompatible_replacement_type) |
| << replacementType << origType; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Process API notes for a variable or property. |
| static void ProcessAPINotes(Sema &S, Decl *D, |
| const api_notes::VariableInfo &info, |
| VersionedInfoMetadata metadata) { |
| // Type override. |
| if (metadata.IsActive && !info.getType().empty() && |
| S.ParseTypeFromStringCallback) { |
| auto parsedType = S.ParseTypeFromStringCallback(info.getType(), |
| "<API Notes>", |
| D->getLocation()); |
| if (parsedType.isUsable()) { |
| QualType type = Sema::GetTypeFromParser(parsedType.get()); |
| auto typeInfo = |
| S.Context.getTrivialTypeSourceInfo(type, D->getLocation()); |
| |
| if (auto var = dyn_cast<VarDecl>(D)) { |
| // Make adjustments to parameter types. |
| if (isa<ParmVarDecl>(var)) { |
| type = S.adjustParameterTypeForObjCAutoRefCount(type, |
| D->getLocation()); |
| type = S.Context.getAdjustedParameterType(type); |
| } |
| |
| if (!checkAPINotesReplacementType(S, var->getLocation(), var->getType(), |
| type)) { |
| var->setType(type); |
| var->setTypeSourceInfo(typeInfo); |
| } |
| } else if (auto property = dyn_cast<ObjCPropertyDecl>(D)) { |
| if (!checkAPINotesReplacementType(S, property->getLocation(), |
| property->getType(), |
| type)) { |
| property->setType(type, typeInfo); |
| } |
| } else { |
| llvm_unreachable("API notes allowed a type on an unknown declaration"); |
| } |
| } |
| } |
| |
| // Nullability. |
| if (auto Nullability = info.getNullability()) { |
| applyNullability(S, D, *Nullability, metadata); |
| } |
| |
| // Handle common entity information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), |
| metadata); |
| } |
| |
| /// Process API notes for a parameter. |
| static void ProcessAPINotes(Sema &S, ParmVarDecl *D, |
| const api_notes::ParamInfo &info, |
| VersionedInfoMetadata metadata) { |
| // noescape |
| if (auto noescape = info.isNoEscape()) { |
| handleAPINotedAttribute<NoEscapeAttr>(S, D, *noescape, metadata, [&] { |
| return NoEscapeAttr::CreateImplicit(S.Context); |
| }); |
| } |
| |
| // Handle common entity information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), |
| metadata); |
| } |
| |
| /// Process API notes for a global variable. |
| static void ProcessAPINotes(Sema &S, VarDecl *D, |
| const api_notes::GlobalVariableInfo &info, |
| VersionedInfoMetadata metadata) { |
| // Handle common entity information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), |
| metadata); |
| } |
| |
| /// Process API notes for an Objective-C property. |
| static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, |
| const api_notes::ObjCPropertyInfo &info, |
| VersionedInfoMetadata metadata) { |
| // Handle common entity information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), |
| metadata); |
| if (auto asAccessors = info.getSwiftImportAsAccessors()) { |
| handleAPINotedAttribute<SwiftImportPropertyAsAccessorsAttr>(S, D, |
| *asAccessors, |
| metadata, [&] { |
| return SwiftImportPropertyAsAccessorsAttr::CreateImplicit(S.Context); |
| }); |
| } |
| } |
| |
| namespace { |
| typedef llvm::PointerUnion<FunctionDecl *, ObjCMethodDecl *> FunctionOrMethod; |
| } |
| |
| /// Process API notes for a function or method. |
| static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, |
| const api_notes::FunctionInfo &info, |
| VersionedInfoMetadata metadata) { |
| // Find the declaration itself. |
| FunctionDecl *FD = AnyFunc.dyn_cast<FunctionDecl *>(); |
| Decl *D = FD; |
| ObjCMethodDecl *MD = 0; |
| if (!D) { |
| MD = AnyFunc.get<ObjCMethodDecl *>(); |
| D = MD; |
| } |
| |
| // Nullability of return type. |
| if (info.NullabilityAudited) { |
| applyNullability(S, D, info.getReturnTypeInfo(), metadata); |
| } |
| |
| // Parameters. |
| unsigned NumParams; |
| if (FD) |
| NumParams = FD->getNumParams(); |
| else |
| NumParams = MD->param_size(); |
| |
| bool anyTypeChanged = false; |
| for (unsigned I = 0; I != NumParams; ++I) { |
| ParmVarDecl *Param; |
| if (FD) |
| Param = FD->getParamDecl(I); |
| else |
| Param = MD->param_begin()[I]; |
| |
| QualType paramTypeBefore = Param->getType(); |
| |
| if (I < info.Params.size()) { |
| ProcessAPINotes(S, Param, info.Params[I], metadata); |
| } |
| |
| // Nullability. |
| if (info.NullabilityAudited) |
| applyNullability(S, Param, info.getParamTypeInfo(I), metadata); |
| |
| if (paramTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr()) |
| anyTypeChanged = true; |
| } |
| |
| // Result type override. |
| QualType overriddenResultType; |
| if (metadata.IsActive && !info.ResultType.empty() && |
| S.ParseTypeFromStringCallback) { |
| auto parsedType = S.ParseTypeFromStringCallback(info.ResultType, |
| "<API Notes>", |
| D->getLocation()); |
| if (parsedType.isUsable()) { |
| QualType resultType = Sema::GetTypeFromParser(parsedType.get()); |
| |
| if (MD) { |
| if (!checkAPINotesReplacementType(S, D->getLocation(), |
| MD->getReturnType(), resultType)) { |
| auto resultTypeInfo = |
| S.Context.getTrivialTypeSourceInfo(resultType, D->getLocation()); |
| MD->setReturnType(resultType); |
| MD->setReturnTypeSourceInfo(resultTypeInfo); |
| } |
| } else if (!checkAPINotesReplacementType(S, FD->getLocation(), |
| FD->getReturnType(), |
| resultType)) { |
| overriddenResultType = resultType; |
| anyTypeChanged = true; |
| } |
| } |
| } |
| |
| // If the result type or any of the parameter types changed for a function |
| // declaration, we have to rebuild the type. |
| if (FD && anyTypeChanged) { |
| if (const auto *fnProtoType = FD->getType()->getAs<FunctionProtoType>()) { |
| if (overriddenResultType.isNull()) |
| overriddenResultType = fnProtoType->getReturnType(); |
| |
| SmallVector<QualType, 4> paramTypes; |
| for (auto param : FD->parameters()) { |
| paramTypes.push_back(param->getType()); |
| } |
| FD->setType(S.Context.getFunctionType(overriddenResultType, |
| paramTypes, |
| fnProtoType->getExtProtoInfo())); |
| } else if (!overriddenResultType.isNull()) { |
| const auto *fnNoProtoType = FD->getType()->castAs<FunctionNoProtoType>(); |
| FD->setType( |
| S.Context.getFunctionNoProtoType(overriddenResultType, |
| fnNoProtoType->getExtInfo())); |
| } |
| } |
| |
| // Handle common entity information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), |
| metadata); |
| } |
| |
| /// Process API notes for a global function. |
| static void ProcessAPINotes(Sema &S, FunctionDecl *D, |
| const api_notes::GlobalFunctionInfo &info, |
| VersionedInfoMetadata metadata) { |
| |
| // Handle common function information. |
| ProcessAPINotes(S, FunctionOrMethod(D), |
| static_cast<const api_notes::FunctionInfo &>(info), metadata); |
| } |
| |
| /// Process API notes for an enumerator. |
| static void ProcessAPINotes(Sema &S, EnumConstantDecl *D, |
| const api_notes::EnumConstantInfo &info, |
| VersionedInfoMetadata metadata) { |
| |
| // Handle common information. |
| ProcessAPINotes(S, D, |
| static_cast<const api_notes::CommonEntityInfo &>(info), |
| metadata); |
| } |
| |
| /// Process API notes for an Objective-C method. |
| static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, |
| const api_notes::ObjCMethodInfo &info, |
| VersionedInfoMetadata metadata) { |
| // Designated initializers. |
| if (info.DesignatedInit) { |
| handleAPINotedAttribute<ObjCDesignatedInitializerAttr>(S, D, true, metadata, |
| [&] { |
| if (ObjCInterfaceDecl *IFace = D->getClassInterface()) { |
| IFace->setHasDesignatedInitializers(); |
| } |
| return ObjCDesignatedInitializerAttr::CreateImplicit(S.Context); |
| }); |
| } |
| |
| // Handle common function information. |
| ProcessAPINotes(S, FunctionOrMethod(D), |
| static_cast<const api_notes::FunctionInfo &>(info), metadata); |
| } |
| |
| /// Process API notes for a tag. |
| static void ProcessAPINotes(Sema &S, TagDecl *D, |
| const api_notes::TagInfo &info, |
| VersionedInfoMetadata metadata) { |
| // Handle common type information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), |
| metadata); |
| } |
| |
| /// Process API notes for a typedef. |
| static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, |
| const api_notes::TypedefInfo &info, |
| VersionedInfoMetadata metadata) { |
| // swift_wrapper |
| using SwiftWrapperKind = api_notes::SwiftWrapperKind; |
| |
| if (auto swiftWrapper = info.SwiftWrapper) { |
| handleAPINotedAttribute<SwiftNewtypeAttr>(S, D, |
| *swiftWrapper != SwiftWrapperKind::None, metadata, |
| [&] { |
| SwiftNewtypeAttr::NewtypeKind kind; |
| switch (*swiftWrapper) { |
| case SwiftWrapperKind::None: |
| llvm_unreachable("Shouldn't build an attribute"); |
| |
| case SwiftWrapperKind::Struct: |
| kind = SwiftNewtypeAttr::NK_Struct; |
| break; |
| |
| case SwiftWrapperKind::Enum: |
| kind = SwiftNewtypeAttr::NK_Enum; |
| break; |
| } |
| return SwiftNewtypeAttr::CreateImplicit( |
| S.Context, |
| SwiftNewtypeAttr::GNU_swift_wrapper, |
| kind); |
| }); |
| } |
| |
| // Handle common type information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), |
| metadata); |
| } |
| |
| /// Process API notes for an Objective-C class or protocol. |
| static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, |
| const api_notes::ObjCContextInfo &info, |
| VersionedInfoMetadata metadata) { |
| |
| // Handle common type information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), |
| metadata); |
| } |
| |
| /// Process API notes for an Objective-C class. |
| static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, |
| const api_notes::ObjCContextInfo &info, |
| VersionedInfoMetadata metadata) { |
| if (auto asNonGeneric = info.getSwiftImportAsNonGeneric()) { |
| handleAPINotedAttribute<SwiftImportAsNonGenericAttr>(S, D, *asNonGeneric, |
| metadata, [&] { |
| return SwiftImportAsNonGenericAttr::CreateImplicit(S.Context); |
| }); |
| } |
| |
| if (auto objcMembers = info.getSwiftObjCMembers()) { |
| handleAPINotedAttribute<SwiftObjCMembersAttr>(S, D, *objcMembers, |
| metadata, [&] { |
| return SwiftObjCMembersAttr::CreateImplicit(S.Context); |
| }); |
| } |
| |
| // Handle information common to Objective-C classes and protocols. |
| ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), info, |
| metadata); |
| } |
| |
| /// Processes all versions of versioned API notes. |
| /// |
| /// Just dispatches to the various ProcessAPINotes functions in this file. |
| template <typename SpecificDecl, typename SpecificInfo> |
| static void ProcessVersionedAPINotes( |
| Sema &S, SpecificDecl *D, |
| const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) { |
| unsigned Selected = Info.getSelected().getValueOr(Info.size()); |
| |
| VersionTuple Version; |
| SpecificInfo InfoSlice; |
| for (unsigned i = 0, e = Info.size(); i != e; ++i) { |
| std::tie(Version, InfoSlice) = Info[i]; |
| auto Active = (i == Selected) ? IsActive : IsNotActive; |
| ProcessAPINotes(S, D, InfoSlice, VersionedInfoMetadata(Version, Active)); |
| } |
| } |
| |
| /// Process API notes that are associated with this declaration, mapping them |
| /// to attributes as appropriate. |
| void Sema::ProcessAPINotes(Decl *D) { |
| if (!D) |
| return; |
| |
| // Globals. |
| if (D->getDeclContext()->isFileContext()) { |
| // Global variables. |
| if (auto VD = dyn_cast<VarDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| auto Info = Reader->lookupGlobalVariable(VD->getName()); |
| ProcessVersionedAPINotes(*this, VD, Info); |
| } |
| |
| return; |
| } |
| |
| // Global functions. |
| if (auto FD = dyn_cast<FunctionDecl>(D)) { |
| if (FD->getDeclName().isIdentifier()) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| auto Info = Reader->lookupGlobalFunction(FD->getName()); |
| ProcessVersionedAPINotes(*this, FD, Info); |
| } |
| } |
| |
| return; |
| } |
| |
| // Objective-C classes. |
| if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| auto Info = Reader->lookupObjCClassInfo(Class->getName()); |
| ProcessVersionedAPINotes(*this, Class, Info); |
| } |
| |
| return; |
| } |
| |
| // Objective-C protocols. |
| if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName()); |
| ProcessVersionedAPINotes(*this, Protocol, Info); |
| } |
| |
| return; |
| } |
| |
| // Tags |
| if (auto Tag = dyn_cast<TagDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| auto Info = Reader->lookupTag(Tag->getName()); |
| ProcessVersionedAPINotes(*this, Tag, Info); |
| } |
| |
| return; |
| } |
| |
| // Typedefs |
| if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| auto Info = Reader->lookupTypedef(Typedef->getName()); |
| ProcessVersionedAPINotes(*this, Typedef, Info); |
| } |
| |
| return; |
| } |
| |
| return; |
| } |
| |
| // Enumerators. |
| if (D->getDeclContext()->getRedeclContext()->isFileContext()) { |
| if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| auto Info = Reader->lookupEnumConstant(EnumConstant->getName()); |
| ProcessVersionedAPINotes(*this, EnumConstant, Info); |
| } |
| |
| return; |
| } |
| } |
| |
| if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(D->getDeclContext())) { |
| // Location function that looks up an Objective-C context. |
| auto GetContext = [&](api_notes::APINotesReader *Reader) |
| -> Optional<api_notes::ContextID> { |
| if (auto Protocol = dyn_cast<ObjCProtocolDecl>(ObjCContainer)) { |
| if (auto Found = Reader->lookupObjCProtocolID(Protocol->getName())) |
| return *Found; |
| |
| return None; |
| } |
| |
| if (auto Impl = dyn_cast<ObjCCategoryImplDecl>(ObjCContainer)) { |
| if (auto Cat = Impl->getCategoryDecl()) |
| ObjCContainer = Cat; |
| else |
| return None; |
| } |
| |
| if (auto Category = dyn_cast<ObjCCategoryDecl>(ObjCContainer)) { |
| if (Category->getClassInterface()) |
| ObjCContainer = Category->getClassInterface(); |
| else |
| return None; |
| } |
| |
| if (auto Impl = dyn_cast<ObjCImplDecl>(ObjCContainer)) { |
| if (Impl->getClassInterface()) |
| ObjCContainer = Impl->getClassInterface(); |
| else |
| return None; |
| } |
| |
| if (auto Class = dyn_cast<ObjCInterfaceDecl>(ObjCContainer)) { |
| if (auto Found = Reader->lookupObjCClassID(Class->getName())) |
| return *Found; |
| |
| return None; |
| |
| } |
| |
| return None; |
| }; |
| |
| // Objective-C methods. |
| if (auto Method = dyn_cast<ObjCMethodDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| if (auto Context = GetContext(Reader)) { |
| // Map the selector. |
| Selector Sel = Method->getSelector(); |
| SmallVector<StringRef, 2> SelPieces; |
| if (Sel.isUnarySelector()) |
| SelPieces.push_back(Sel.getNameForSlot(0)); |
| else { |
| for (unsigned i = 0, n = Sel.getNumArgs(); i != n; ++i) |
| SelPieces.push_back(Sel.getNameForSlot(i)); |
| } |
| |
| api_notes::ObjCSelectorRef SelectorRef; |
| SelectorRef.NumPieces = Sel.getNumArgs(); |
| SelectorRef.Identifiers = SelPieces; |
| |
| auto Info = Reader->lookupObjCMethod(*Context, SelectorRef, |
| Method->isInstanceMethod()); |
| ProcessVersionedAPINotes(*this, Method, Info); |
| } |
| } |
| } |
| |
| // Objective-C properties. |
| if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| if (auto Context = GetContext(Reader)) { |
| bool isInstanceProperty = |
| (Property->getPropertyAttributesAsWritten() & |
| ObjCPropertyDecl::OBJC_PR_class) == 0; |
| auto Info = Reader->lookupObjCProperty(*Context, Property->getName(), |
| isInstanceProperty); |
| ProcessVersionedAPINotes(*this, Property, Info); |
| } |
| } |
| |
| return; |
| } |
| |
| return; |
| } |
| } |