| //===--- 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; |
| using clang::api_notes::VersionedInfoRole; |
| |
| /// 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, |
| VersionedInfoRole role) { |
| bool overrideExisting; |
| switch (role) { |
| case VersionedInfoRole::AugmentSource: |
| overrideExisting = false; |
| break; |
| |
| case VersionedInfoRole::ReplaceSource: |
| overrideExisting = true; |
| break; |
| |
| case VersionedInfoRole::Versioned: |
| // FIXME: Record versioned info? |
| 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); |
| 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 { |
| /// 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. |
| /// \param getExistingAttr Get an existing, matching attribute on the given |
| /// declaration. |
| template<typename A> |
| void handleAPINotedAttribute( |
| Sema &S, Decl *D, bool shouldAddAttribute, |
| VersionedInfoRole role, |
| llvm::function_ref<A *()> createAttr, |
| llvm::function_ref<specific_attr_iterator<A>(Decl *)> getExistingAttr) { |
| switch (role) { |
| case VersionedInfoRole::AugmentSource: |
| // If we're not adding an attribute, there's nothing to do. |
| if (!shouldAddAttribute) return; |
| |
| // If the attribute is already present, we're done. |
| if (getExistingAttr(D) != D->specific_attr_end<A>()) return; |
| |
| // Add the attribute. |
| if (auto attr = createAttr()) |
| D->addAttr(attr); |
| break; |
| |
| case VersionedInfoRole::ReplaceSource: { |
| auto end = D->specific_attr_end<A>(); |
| auto existing = getExistingAttr(D); |
| if (existing != end) { |
| // Remove the existing attribute. |
| D->getAttrs().erase(existing.getCurrent()); |
| } |
| |
| // If we're supposed to add a new attribute, do so. |
| if (shouldAddAttribute) { |
| if (auto attr = createAttr()) { |
| D->addAttr(attr); |
| } |
| } |
| break; |
| } |
| |
| case VersionedInfoRole::Versioned: |
| // FIXME: Retain versioned attributes separately. |
| break; |
| } |
| } |
| |
| template<typename A> |
| void handleAPINotedAttribute( |
| Sema &S, Decl *D, bool shouldAddAttribute, |
| VersionedInfoRole role, |
| llvm::function_ref<A *()> createAttr) { |
| handleAPINotedAttribute<A>(S, D, shouldAddAttribute, role, createAttr, |
| [](Decl *decl) { |
| return decl->specific_attr_begin<A>(); |
| }); |
| } |
| } |
| |
| static void ProcessAPINotes(Sema &S, Decl *D, |
| const api_notes::CommonEntityInfo &info, |
| VersionedInfoRole role) { |
| // Availability |
| if (info.Unavailable) { |
| handleAPINotedAttribute<UnavailableAttr>(S, D, true, role, |
| [&] { |
| return UnavailableAttr::CreateImplicit(S.Context, |
| CopyString(S.Context, |
| info.UnavailableMsg)); |
| }); |
| } |
| |
| if (info.UnavailableInSwift) { |
| handleAPINotedAttribute<AvailabilityAttr>(S, D, true, role, [&] { |
| 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, role, [&] { |
| return SwiftPrivateAttr::CreateImplicit(S.Context); |
| }); |
| } |
| |
| // swift_name |
| if (!info.SwiftName.empty()) { |
| handleAPINotedAttribute<SwiftNameAttr>(S, D, true, role, |
| [&]() -> 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, |
| VersionedInfoRole role) { |
| // swift_bridge |
| if (auto swiftBridge = info.getSwiftBridge()) { |
| handleAPINotedAttribute<SwiftBridgeAttr>(S, D, !swiftBridge->empty(), role, |
| [&] { |
| return SwiftBridgeAttr::CreateImplicit(S.Context, |
| CopyString(S.Context, |
| *swiftBridge)); |
| }); |
| } |
| |
| // ns_error_domain |
| if (auto nsErrorDomain = info.getNSErrorDomain()) { |
| handleAPINotedAttribute<NSErrorDomainAttr>(S, D, !nsErrorDomain->empty(), |
| role, [&] { |
| return NSErrorDomainAttr::CreateImplicit( |
| S.Context, |
| &S.Context.Idents.get(*nsErrorDomain)); |
| }); |
| } |
| |
| ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), |
| role); |
| } |
| |
| /// 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, |
| VersionedInfoRole role) { |
| // Type override. |
| if (role != VersionedInfoRole::Versioned && |
| !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, role); |
| } |
| |
| // Handle common entity information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), |
| role); |
| } |
| |
| /// Process API notes for a parameter. |
| static void ProcessAPINotes(Sema &S, ParmVarDecl *D, |
| const api_notes::ParamInfo &info, |
| VersionedInfoRole role) { |
| // noescape |
| if (auto noescape = info.isNoEscape()) { |
| handleAPINotedAttribute<NoEscapeAttr>(S, D, *noescape, role, [&] { |
| return NoEscapeAttr::CreateImplicit(S.Context); |
| }); |
| } |
| |
| // Handle common entity information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), |
| role); |
| } |
| |
| /// Process API notes for a global variable. |
| static void ProcessAPINotes(Sema &S, VarDecl *D, |
| const api_notes::GlobalVariableInfo &info, |
| VersionedInfoRole role) { |
| // Handle common entity information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), |
| role); |
| } |
| |
| /// Process API notes for an Objective-C property. |
| static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, |
| const api_notes::ObjCPropertyInfo &info, |
| VersionedInfoRole role) { |
| // Handle common entity information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), |
| role); |
| if (auto asAccessors = info.getSwiftImportAsAccessors()) { |
| handleAPINotedAttribute<SwiftImportPropertyAsAccessorsAttr>(S, D, |
| *asAccessors, |
| role, [&] { |
| 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, |
| VersionedInfoRole role) { |
| // 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(), role); |
| } |
| |
| // 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], role); |
| } |
| |
| // Nullability. |
| if (info.NullabilityAudited) |
| applyNullability(S, Param, info.getParamTypeInfo(I), role); |
| |
| if (paramTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr()) |
| anyTypeChanged = true; |
| } |
| |
| // Result type override. |
| QualType overriddenResultType; |
| if (role != VersionedInfoRole::Versioned && !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), |
| role); |
| } |
| |
| /// Process API notes for a global function. |
| static void ProcessAPINotes(Sema &S, FunctionDecl *D, |
| const api_notes::GlobalFunctionInfo &info, |
| VersionedInfoRole role) { |
| |
| // Handle common function information. |
| ProcessAPINotes(S, FunctionOrMethod(D), |
| static_cast<const api_notes::FunctionInfo &>(info), role); |
| } |
| |
| /// Process API notes for an enumerator. |
| static void ProcessAPINotes(Sema &S, EnumConstantDecl *D, |
| const api_notes::EnumConstantInfo &info, |
| VersionedInfoRole role) { |
| |
| // Handle common information. |
| ProcessAPINotes(S, D, |
| static_cast<const api_notes::CommonEntityInfo &>(info), |
| role); |
| } |
| |
| /// Process API notes for an Objective-C method. |
| static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, |
| const api_notes::ObjCMethodInfo &info, |
| VersionedInfoRole role) { |
| // Designated initializers. |
| if (info.DesignatedInit) { |
| handleAPINotedAttribute<ObjCDesignatedInitializerAttr>(S, D, true, role, [&] { |
| if (ObjCInterfaceDecl *IFace = D->getClassInterface()) { |
| IFace->setHasDesignatedInitializers(); |
| } |
| return ObjCDesignatedInitializerAttr::CreateImplicit(S.Context); |
| }); |
| } |
| |
| // FIXME: This doesn't work well with versioned API notes. |
| if (role == VersionedInfoRole::AugmentSource && |
| info.getFactoryAsInitKind() |
| == api_notes::FactoryAsInitKind::AsClassMethod && |
| !D->getAttr<SwiftNameAttr>()) { |
| D->addAttr(SwiftSuppressFactoryAsInitAttr::CreateImplicit(S.Context)); |
| } |
| |
| // Handle common function information. |
| ProcessAPINotes(S, FunctionOrMethod(D), |
| static_cast<const api_notes::FunctionInfo &>(info), role); |
| } |
| |
| /// Process API notes for a tag. |
| static void ProcessAPINotes(Sema &S, TagDecl *D, |
| const api_notes::TagInfo &info, |
| VersionedInfoRole role) { |
| // Handle common type information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), |
| role); |
| } |
| |
| /// Process API notes for a typedef. |
| static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, |
| const api_notes::TypedefInfo &info, |
| VersionedInfoRole role) { |
| // swift_wrapper |
| using SwiftWrapperKind = api_notes::SwiftWrapperKind; |
| |
| if (auto swiftWrapper = info.SwiftWrapper) { |
| handleAPINotedAttribute<SwiftNewtypeAttr>(S, D, |
| *swiftWrapper != SwiftWrapperKind::None, role, |
| [&] { |
| 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), |
| role); |
| } |
| |
| /// Process API notes for an Objective-C class or protocol. |
| static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, |
| const api_notes::ObjCContextInfo &info, |
| VersionedInfoRole role) { |
| |
| // Handle common type information. |
| ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), |
| role); |
| } |
| |
| /// Process API notes for an Objective-C class. |
| static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, |
| const api_notes::ObjCContextInfo &info, |
| VersionedInfoRole role) { |
| // Handle information common to Objective-C classes and protocols. |
| ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), info, role); |
| } |
| |
| /// 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())) { |
| if (auto Info = Reader->lookupGlobalVariable(VD->getName())) { |
| ::ProcessAPINotes(*this, VD, *Info, Info.getSelectedRole()); |
| } |
| } |
| |
| return; |
| } |
| |
| // Global functions. |
| if (auto FD = dyn_cast<FunctionDecl>(D)) { |
| if (FD->getDeclName().isIdentifier()) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| if (auto Info = Reader->lookupGlobalFunction(FD->getName())) { |
| ::ProcessAPINotes(*this, FD, *Info, Info.getSelectedRole()); |
| } |
| } |
| } |
| |
| return; |
| } |
| |
| // Objective-C classes. |
| if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| if (auto Info = Reader->lookupObjCClassInfo(Class->getName())) { |
| ::ProcessAPINotes(*this, Class, *Info, Info.getSelectedRole()); |
| } |
| } |
| |
| return; |
| } |
| |
| // Objective-C protocols. |
| if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| if (auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName())) { |
| ::ProcessAPINotes(*this, Protocol, *Info, Info.getSelectedRole()); |
| } |
| } |
| |
| return; |
| } |
| |
| // Tags |
| if (auto Tag = dyn_cast<TagDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| if (auto Info = Reader->lookupTag(Tag->getName())) { |
| ::ProcessAPINotes(*this, Tag, *Info, Info.getSelectedRole()); |
| } |
| } |
| |
| return; |
| } |
| |
| // Typedefs |
| if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| if (auto Info = Reader->lookupTypedef(Typedef->getName())) { |
| ::ProcessAPINotes(*this, Typedef, *Info, Info.getSelectedRole()); |
| } |
| } |
| |
| return; |
| } |
| |
| return; |
| } |
| |
| // Enumerators. |
| if (D->getDeclContext()->getRedeclContext()->isFileContext()) { |
| if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) { |
| for (auto Reader : APINotes.findAPINotes(D->getLocation())) { |
| if (auto Info = Reader->lookupEnumConstant(EnumConstant->getName())) { |
| ::ProcessAPINotes(*this, EnumConstant, *Info, Info.getSelectedRole()); |
| } |
| } |
| |
| 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; |
| |
| if (auto Info = Reader->lookupObjCMethod(*Context, SelectorRef, |
| Method->isInstanceMethod())){ |
| ::ProcessAPINotes(*this, Method, *Info, Info.getSelectedRole()); |
| } |
| } |
| } |
| } |
| |
| // 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; |
| if (auto Info = Reader->lookupObjCProperty(*Context, |
| Property->getName(), |
| isInstanceProperty)) { |
| ::ProcessAPINotes(*this, Property, *Info, Info.getSelectedRole()); |
| } |
| } |
| } |
| |
| return; |
| } |
| |
| return; |
| } |
| } |