1//===--- SemaAPINotes.cpp - API Notes Handling ----------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the mapping from API notes to declaration attributes.
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/APINotes/APINotesReader.h"
14#include "clang/AST/Decl.h"
15#include "clang/AST/DeclObjC.h"
16#include "clang/Basic/SourceLocation.h"
17#include "clang/Lex/Lexer.h"
18#include "clang/Sema/SemaInternal.h"
19
20using namespace clang;
21
22namespace {
23enum class IsActive_t : bool { Inactive, Active };
24enum class IsSubstitution_t : bool { Original, Replacement };
25
26struct VersionedInfoMetadata {
27 /// An empty version refers to unversioned metadata.
28 VersionTuple Version;
29 unsigned IsActive : 1;
30 unsigned IsReplacement : 1;
31
32 VersionedInfoMetadata(VersionTuple Version, IsActive_t Active,
33 IsSubstitution_t Replacement)
34 : Version(Version), IsActive(Active == IsActive_t::Active),
35 IsReplacement(Replacement == IsSubstitution_t::Replacement) {}
36};
37} // end anonymous namespace
38
39/// Determine whether this is a multi-level pointer type.
40static bool isIndirectPointerType(QualType Type) {
41 QualType Pointee = Type->getPointeeType();
42 if (Pointee.isNull())
43 return false;
44
45 return Pointee->isAnyPointerType() || Pointee->isObjCObjectPointerType() ||
46 Pointee->isMemberPointerType();
47}
48
49/// Apply nullability to the given declaration.
50static void applyNullability(Sema &S, Decl *D, NullabilityKind Nullability,
51 VersionedInfoMetadata Metadata) {
52 if (!Metadata.IsActive)
53 return;
54
55 auto GetModified =
56 [&](Decl *D, QualType QT,
57 NullabilityKind Nullability) -> std::optional<QualType> {
58 QualType Original = QT;
59 S.CheckImplicitNullabilityTypeSpecifier(Type&: QT, Nullability, DiagLoc: D->getLocation(),
60 AllowArrayTypes: isa<ParmVarDecl>(Val: D),
61 /*OverrideExisting=*/true);
62 return (QT.getTypePtr() != Original.getTypePtr()) ? std::optional(QT)
63 : std::nullopt;
64 };
65
66 if (auto Function = dyn_cast<FunctionDecl>(Val: D)) {
67 if (auto Modified =
68 GetModified(D, Function->getReturnType(), Nullability)) {
69 const FunctionType *FnType = Function->getType()->castAs<FunctionType>();
70 if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(Val: FnType))
71 Function->setType(S.Context.getFunctionType(
72 ResultTy: *Modified, Args: proto->getParamTypes(), EPI: proto->getExtProtoInfo()));
73 else
74 Function->setType(
75 S.Context.getFunctionNoProtoType(ResultTy: *Modified, Info: FnType->getExtInfo()));
76 }
77 } else if (auto Method = dyn_cast<ObjCMethodDecl>(Val: D)) {
78 if (auto Modified = GetModified(D, Method->getReturnType(), Nullability)) {
79 Method->setReturnType(*Modified);
80
81 // Make it a context-sensitive keyword if we can.
82 if (!isIndirectPointerType(*Modified))
83 Method->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
84 Method->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
85 }
86 } else if (auto Value = dyn_cast<ValueDecl>(Val: D)) {
87 if (auto Modified = GetModified(D, Value->getType(), Nullability)) {
88 Value->setType(*Modified);
89
90 // Make it a context-sensitive keyword if we can.
91 if (auto Parm = dyn_cast<ParmVarDecl>(Val: D)) {
92 if (Parm->isObjCMethodParameter() && !isIndirectPointerType(*Modified))
93 Parm->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
94 Parm->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
95 }
96 }
97 } else if (auto Property = dyn_cast<ObjCPropertyDecl>(Val: D)) {
98 if (auto Modified = GetModified(D, Property->getType(), Nullability)) {
99 Property->setType(T: *Modified, TSI: Property->getTypeSourceInfo());
100
101 // Make it a property attribute if we can.
102 if (!isIndirectPointerType(*Modified))
103 Property->setPropertyAttributes(
104 ObjCPropertyAttribute::kind_null_resettable);
105 }
106 }
107}
108
109/// Copy a string into ASTContext-allocated memory.
110static StringRef ASTAllocateString(ASTContext &Ctx, StringRef String) {
111 void *mem = Ctx.Allocate(Size: String.size(), Align: alignof(char *));
112 memcpy(dest: mem, src: String.data(), n: String.size());
113 return StringRef(static_cast<char *>(mem), String.size());
114}
115
116static AttributeCommonInfo getPlaceholderAttrInfo() {
117 return AttributeCommonInfo(SourceRange(),
118 AttributeCommonInfo::UnknownAttribute,
119 {AttributeCommonInfo::AS_GNU,
120 /*Spelling*/ 0, /*IsAlignas*/ false,
121 /*IsRegularKeywordAttribute*/ false});
122}
123
124namespace {
125template <typename A> struct AttrKindFor {};
126
127#define ATTR(X) \
128 template <> struct AttrKindFor<X##Attr> { \
129 static const attr::Kind value = attr::X; \
130 };
131#include "clang/Basic/AttrList.inc"
132
133/// Handle an attribute introduced by API notes.
134///
135/// \param IsAddition Whether we should add a new attribute
136/// (otherwise, we might remove an existing attribute).
137/// \param CreateAttr Create the new attribute to be added.
138template <typename A>
139void handleAPINotedAttribute(
140 Sema &S, Decl *D, bool IsAddition, VersionedInfoMetadata Metadata,
141 llvm::function_ref<A *()> CreateAttr,
142 llvm::function_ref<Decl::attr_iterator(const Decl *)> GetExistingAttr) {
143 if (Metadata.IsActive) {
144 auto Existing = GetExistingAttr(D);
145 if (Existing != D->attr_end()) {
146 // Remove the existing attribute, and treat it as a superseded
147 // non-versioned attribute.
148 auto *Versioned = SwiftVersionedAdditionAttr::CreateImplicit(
149 S.Context, Metadata.Version, *Existing, /*IsReplacedByActive*/ true);
150
151 D->getAttrs().erase(Existing);
152 D->addAttr(A: Versioned);
153 }
154
155 // If we're supposed to add a new attribute, do so.
156 if (IsAddition) {
157 if (auto Attr = CreateAttr())
158 D->addAttr(A: Attr);
159 }
160
161 return;
162 }
163 if (IsAddition) {
164 if (auto Attr = CreateAttr()) {
165 auto *Versioned = SwiftVersionedAdditionAttr::CreateImplicit(
166 S.Context, Metadata.Version, Attr,
167 /*IsReplacedByActive*/ Metadata.IsReplacement);
168 D->addAttr(A: Versioned);
169 }
170 } else {
171 // FIXME: This isn't preserving enough information for things like
172 // availability, where we're trying to remove a /specific/ kind of
173 // attribute.
174 auto *Versioned = SwiftVersionedRemovalAttr::CreateImplicit(
175 S.Context, Metadata.Version, AttrKindFor<A>::value,
176 /*IsReplacedByActive*/ Metadata.IsReplacement);
177 D->addAttr(A: Versioned);
178 }
179}
180
181template <typename A>
182void handleAPINotedAttribute(Sema &S, Decl *D, bool ShouldAddAttribute,
183 VersionedInfoMetadata Metadata,
184 llvm::function_ref<A *()> CreateAttr) {
185 handleAPINotedAttribute<A>(
186 S, D, ShouldAddAttribute, Metadata, CreateAttr, [](const Decl *D) {
187 return llvm::find_if(D->attrs(),
188 [](const Attr *Next) { return isa<A>(Next); });
189 });
190}
191} // namespace
192
193template <typename A>
194static void handleAPINotedRetainCountAttribute(Sema &S, Decl *D,
195 bool ShouldAddAttribute,
196 VersionedInfoMetadata Metadata) {
197 // The template argument has a default to make the "removal" case more
198 // concise; it doesn't matter /which/ attribute is being removed.
199 handleAPINotedAttribute<A>(
200 S, D, ShouldAddAttribute, Metadata,
201 [&] { return new (S.Context) A(S.Context, getPlaceholderAttrInfo()); },
202 [](const Decl *D) -> Decl::attr_iterator {
203 return llvm::find_if(D->attrs(), [](const Attr *Next) -> bool {
204 return isa<CFReturnsRetainedAttr>(Next) ||
205 isa<CFReturnsNotRetainedAttr>(Next) ||
206 isa<NSReturnsRetainedAttr>(Next) ||
207 isa<NSReturnsNotRetainedAttr>(Next) ||
208 isa<CFAuditedTransferAttr>(Next);
209 });
210 });
211}
212
213static void handleAPINotedRetainCountConvention(
214 Sema &S, Decl *D, VersionedInfoMetadata Metadata,
215 std::optional<api_notes::RetainCountConventionKind> Convention) {
216 if (!Convention)
217 return;
218 switch (*Convention) {
219 case api_notes::RetainCountConventionKind::None:
220 if (isa<FunctionDecl>(Val: D)) {
221 handleAPINotedRetainCountAttribute<CFUnknownTransferAttr>(
222 S, D, /*shouldAddAttribute*/ true, Metadata);
223 } else {
224 handleAPINotedRetainCountAttribute<CFReturnsRetainedAttr>(
225 S, D, /*shouldAddAttribute*/ false, Metadata);
226 }
227 break;
228 case api_notes::RetainCountConventionKind::CFReturnsRetained:
229 handleAPINotedRetainCountAttribute<CFReturnsRetainedAttr>(
230 S, D, /*shouldAddAttribute*/ true, Metadata);
231 break;
232 case api_notes::RetainCountConventionKind::CFReturnsNotRetained:
233 handleAPINotedRetainCountAttribute<CFReturnsNotRetainedAttr>(
234 S, D, /*shouldAddAttribute*/ true, Metadata);
235 break;
236 case api_notes::RetainCountConventionKind::NSReturnsRetained:
237 handleAPINotedRetainCountAttribute<NSReturnsRetainedAttr>(
238 S, D, /*shouldAddAttribute*/ true, Metadata);
239 break;
240 case api_notes::RetainCountConventionKind::NSReturnsNotRetained:
241 handleAPINotedRetainCountAttribute<NSReturnsNotRetainedAttr>(
242 S, D, /*shouldAddAttribute*/ true, Metadata);
243 break;
244 }
245}
246
247static void ProcessAPINotes(Sema &S, Decl *D,
248 const api_notes::CommonEntityInfo &Info,
249 VersionedInfoMetadata Metadata) {
250 // Availability
251 if (Info.Unavailable) {
252 handleAPINotedAttribute<UnavailableAttr>(S, D, true, Metadata, [&] {
253 return new (S.Context)
254 UnavailableAttr(S.Context, getPlaceholderAttrInfo(),
255 ASTAllocateString(S.Context, Info.UnavailableMsg));
256 });
257 }
258
259 if (Info.UnavailableInSwift) {
260 handleAPINotedAttribute<AvailabilityAttr>(
261 S, D, true, Metadata,
262 [&] {
263 return new (S.Context) AvailabilityAttr(
264 S.Context, getPlaceholderAttrInfo(),
265 &S.Context.Idents.get("swift"), VersionTuple(), VersionTuple(),
266 VersionTuple(),
267 /*Unavailable=*/true,
268 ASTAllocateString(S.Context, Info.UnavailableMsg),
269 /*Strict=*/false,
270 /*Replacement=*/StringRef(),
271 /*Priority=*/Sema::AP_Explicit);
272 },
273 [](const Decl *D) {
274 return llvm::find_if(D->attrs(), [](const Attr *next) -> bool {
275 if (const auto *AA = dyn_cast<AvailabilityAttr>(next))
276 if (const auto *II = AA->getPlatform())
277 return II->isStr("swift");
278 return false;
279 });
280 });
281 }
282
283 // swift_private
284 if (auto SwiftPrivate = Info.isSwiftPrivate()) {
285 handleAPINotedAttribute<SwiftPrivateAttr>(
286 S, D, *SwiftPrivate, Metadata, [&] {
287 return new (S.Context)
288 SwiftPrivateAttr(S.Context, getPlaceholderAttrInfo());
289 });
290 }
291
292 // swift_name
293 if (!Info.SwiftName.empty()) {
294 handleAPINotedAttribute<SwiftNameAttr>(
295 S, D, true, Metadata, [&]() -> SwiftNameAttr * {
296 AttributeFactory AF{};
297 AttributePool AP{AF};
298 auto &C = S.getASTContext();
299 ParsedAttr *SNA =
300 AP.create(&C.Idents.get("swift_name"), SourceRange(), nullptr,
301 SourceLocation(), nullptr, nullptr, nullptr,
302 ParsedAttr::Form::GNU());
303
304 if (!S.DiagnoseSwiftName(D, Info.SwiftName, D->getLocation(), *SNA,
305 /*IsAsync=*/false))
306 return nullptr;
307
308 return new (S.Context)
309 SwiftNameAttr(S.Context, getPlaceholderAttrInfo(),
310 ASTAllocateString(S.Context, Info.SwiftName));
311 });
312 }
313}
314
315static void ProcessAPINotes(Sema &S, Decl *D,
316 const api_notes::CommonTypeInfo &Info,
317 VersionedInfoMetadata Metadata) {
318 // swift_bridge
319 if (auto SwiftBridge = Info.getSwiftBridge()) {
320 handleAPINotedAttribute<SwiftBridgeAttr>(
321 S, D, !SwiftBridge->empty(), Metadata, [&] {
322 return new (S.Context)
323 SwiftBridgeAttr(S.Context, getPlaceholderAttrInfo(),
324 ASTAllocateString(S.Context, *SwiftBridge));
325 });
326 }
327
328 // ns_error_domain
329 if (auto NSErrorDomain = Info.getNSErrorDomain()) {
330 handleAPINotedAttribute<NSErrorDomainAttr>(
331 S, D, !NSErrorDomain->empty(), Metadata, [&] {
332 return new (S.Context)
333 NSErrorDomainAttr(S.Context, getPlaceholderAttrInfo(),
334 &S.Context.Idents.get(*NSErrorDomain));
335 });
336 }
337
338 ProcessAPINotes(S, D, Info: static_cast<const api_notes::CommonEntityInfo &>(Info),
339 Metadata);
340}
341
342/// Check that the replacement type provided by API notes is reasonable.
343///
344/// This is a very weak form of ABI check.
345static bool checkAPINotesReplacementType(Sema &S, SourceLocation Loc,
346 QualType OrigType,
347 QualType ReplacementType) {
348 if (S.Context.getTypeSize(T: OrigType) !=
349 S.Context.getTypeSize(T: ReplacementType)) {
350 S.Diag(Loc, diag::err_incompatible_replacement_type)
351 << ReplacementType << OrigType;
352 return true;
353 }
354
355 return false;
356}
357
358/// Process API notes for a variable or property.
359static void ProcessAPINotes(Sema &S, Decl *D,
360 const api_notes::VariableInfo &Info,
361 VersionedInfoMetadata Metadata) {
362 // Type override.
363 if (Metadata.IsActive && !Info.getType().empty() &&
364 S.ParseTypeFromStringCallback) {
365 auto ParsedType = S.ParseTypeFromStringCallback(
366 Info.getType(), "<API Notes>", D->getLocation());
367 if (ParsedType.isUsable()) {
368 QualType Type = Sema::GetTypeFromParser(Ty: ParsedType.get());
369 auto TypeInfo =
370 S.Context.getTrivialTypeSourceInfo(T: Type, Loc: D->getLocation());
371
372 if (auto Var = dyn_cast<VarDecl>(Val: D)) {
373 // Make adjustments to parameter types.
374 if (isa<ParmVarDecl>(Val: Var)) {
375 Type = S.AdjustParameterTypeForObjCAutoRefCount(
376 T: Type, NameLoc: D->getLocation(), TSInfo: TypeInfo);
377 Type = S.Context.getAdjustedParameterType(T: Type);
378 }
379
380 if (!checkAPINotesReplacementType(S, Var->getLocation(), Var->getType(),
381 Type)) {
382 Var->setType(Type);
383 Var->setTypeSourceInfo(TypeInfo);
384 }
385 } else if (auto Property = dyn_cast<ObjCPropertyDecl>(Val: D)) {
386 if (!checkAPINotesReplacementType(S, Property->getLocation(),
387 Property->getType(), Type))
388 Property->setType(T: Type, TSI: TypeInfo);
389
390 } else
391 llvm_unreachable("API notes allowed a type on an unknown declaration");
392 }
393 }
394
395 // Nullability.
396 if (auto Nullability = Info.getNullability())
397 applyNullability(S, D, Nullability: *Nullability, Metadata);
398
399 // Handle common entity information.
400 ProcessAPINotes(S, D, Info: static_cast<const api_notes::CommonEntityInfo &>(Info),
401 Metadata);
402}
403
404/// Process API notes for a parameter.
405static void ProcessAPINotes(Sema &S, ParmVarDecl *D,
406 const api_notes::ParamInfo &Info,
407 VersionedInfoMetadata Metadata) {
408 // noescape
409 if (auto NoEscape = Info.isNoEscape())
410 handleAPINotedAttribute<NoEscapeAttr>(S, D, *NoEscape, Metadata, [&] {
411 return new (S.Context) NoEscapeAttr(S.Context, getPlaceholderAttrInfo());
412 });
413
414 // Retain count convention
415 handleAPINotedRetainCountConvention(S, D, Metadata,
416 Info.getRetainCountConvention());
417
418 // Handle common entity information.
419 ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info),
420 Metadata);
421}
422
423/// Process API notes for a global variable.
424static void ProcessAPINotes(Sema &S, VarDecl *D,
425 const api_notes::GlobalVariableInfo &Info,
426 VersionedInfoMetadata metadata) {
427 // Handle common entity information.
428 ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info),
429 metadata);
430}
431
432/// Process API notes for an Objective-C property.
433static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D,
434 const api_notes::ObjCPropertyInfo &Info,
435 VersionedInfoMetadata Metadata) {
436 // Handle common entity information.
437 ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info),
438 Metadata);
439
440 if (auto AsAccessors = Info.getSwiftImportAsAccessors()) {
441 handleAPINotedAttribute<SwiftImportPropertyAsAccessorsAttr>(
442 S, D, *AsAccessors, Metadata, [&] {
443 return new (S.Context) SwiftImportPropertyAsAccessorsAttr(
444 S.Context, getPlaceholderAttrInfo());
445 });
446 }
447}
448
449namespace {
450typedef llvm::PointerUnion<FunctionDecl *, ObjCMethodDecl *> FunctionOrMethod;
451}
452
453/// Process API notes for a function or method.
454static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc,
455 const api_notes::FunctionInfo &Info,
456 VersionedInfoMetadata Metadata) {
457 // Find the declaration itself.
458 FunctionDecl *FD = AnyFunc.dyn_cast<FunctionDecl *>();
459 Decl *D = FD;
460 ObjCMethodDecl *MD = nullptr;
461 if (!D) {
462 MD = AnyFunc.get<ObjCMethodDecl *>();
463 D = MD;
464 }
465
466 assert((FD || MD) && "Expecting Function or ObjCMethod");
467
468 // Nullability of return type.
469 if (Info.NullabilityAudited)
470 applyNullability(S, D, Nullability: Info.getReturnTypeInfo(), Metadata);
471
472 // Parameters.
473 unsigned NumParams = FD ? FD->getNumParams() : MD->param_size();
474
475 bool AnyTypeChanged = false;
476 for (unsigned I = 0; I != NumParams; ++I) {
477 ParmVarDecl *Param = FD ? FD->getParamDecl(i: I) : MD->param_begin()[I];
478 QualType ParamTypeBefore = Param->getType();
479
480 if (I < Info.Params.size())
481 ProcessAPINotes(S, D: Param, Info: Info.Params[I], Metadata);
482
483 // Nullability.
484 if (Info.NullabilityAudited)
485 applyNullability(S, Param, Info.getParamTypeInfo(index: I), Metadata);
486
487 if (ParamTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr())
488 AnyTypeChanged = true;
489 }
490
491 // Result type override.
492 QualType OverriddenResultType;
493 if (Metadata.IsActive && !Info.ResultType.empty() &&
494 S.ParseTypeFromStringCallback) {
495 auto ParsedType = S.ParseTypeFromStringCallback(
496 Info.ResultType, "<API Notes>", D->getLocation());
497 if (ParsedType.isUsable()) {
498 QualType ResultType = Sema::GetTypeFromParser(Ty: ParsedType.get());
499
500 if (MD) {
501 if (!checkAPINotesReplacementType(S, Loc: D->getLocation(),
502 OrigType: MD->getReturnType(), ReplacementType: ResultType)) {
503 auto ResultTypeInfo =
504 S.Context.getTrivialTypeSourceInfo(T: ResultType, Loc: D->getLocation());
505 MD->setReturnType(ResultType);
506 MD->setReturnTypeSourceInfo(ResultTypeInfo);
507 }
508 } else if (!checkAPINotesReplacementType(
509 S, FD->getLocation(), FD->getReturnType(), ResultType)) {
510 OverriddenResultType = ResultType;
511 AnyTypeChanged = true;
512 }
513 }
514 }
515
516 // If the result type or any of the parameter types changed for a function
517 // declaration, we have to rebuild the type.
518 if (FD && AnyTypeChanged) {
519 if (const auto *fnProtoType = FD->getType()->getAs<FunctionProtoType>()) {
520 if (OverriddenResultType.isNull())
521 OverriddenResultType = fnProtoType->getReturnType();
522
523 SmallVector<QualType, 4> ParamTypes;
524 for (auto Param : FD->parameters())
525 ParamTypes.push_back(Elt: Param->getType());
526
527 FD->setType(S.Context.getFunctionType(ResultTy: OverriddenResultType, Args: ParamTypes,
528 EPI: fnProtoType->getExtProtoInfo()));
529 } else if (!OverriddenResultType.isNull()) {
530 const auto *FnNoProtoType = FD->getType()->castAs<FunctionNoProtoType>();
531 FD->setType(S.Context.getFunctionNoProtoType(
532 OverriddenResultType, FnNoProtoType->getExtInfo()));
533 }
534 }
535
536 // Retain count convention
537 handleAPINotedRetainCountConvention(S, D, Metadata,
538 Convention: Info.getRetainCountConvention());
539
540 // Handle common entity information.
541 ProcessAPINotes(S, D, Info: static_cast<const api_notes::CommonEntityInfo &>(Info),
542 Metadata);
543}
544
545/// Process API notes for a global function.
546static void ProcessAPINotes(Sema &S, FunctionDecl *D,
547 const api_notes::GlobalFunctionInfo &Info,
548 VersionedInfoMetadata Metadata) {
549 // Handle common function information.
550 ProcessAPINotes(S, AnyFunc: FunctionOrMethod(D),
551 Info: static_cast<const api_notes::FunctionInfo &>(Info), Metadata);
552}
553
554/// Process API notes for an enumerator.
555static void ProcessAPINotes(Sema &S, EnumConstantDecl *D,
556 const api_notes::EnumConstantInfo &Info,
557 VersionedInfoMetadata Metadata) {
558 // Handle common information.
559 ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info),
560 Metadata);
561}
562
563/// Process API notes for an Objective-C method.
564static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D,
565 const api_notes::ObjCMethodInfo &Info,
566 VersionedInfoMetadata Metadata) {
567 // Designated initializers.
568 if (Info.DesignatedInit) {
569 handleAPINotedAttribute<ObjCDesignatedInitializerAttr>(
570 S, D, true, Metadata, [&] {
571 if (ObjCInterfaceDecl *IFace = D->getClassInterface())
572 IFace->setHasDesignatedInitializers();
573
574 return new (S.Context) ObjCDesignatedInitializerAttr(
575 S.Context, getPlaceholderAttrInfo());
576 });
577 }
578
579 // Handle common function information.
580 ProcessAPINotes(S, AnyFunc: FunctionOrMethod(D),
581 Info: static_cast<const api_notes::FunctionInfo &>(Info), Metadata);
582}
583
584/// Process API notes for a tag.
585static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &Info,
586 VersionedInfoMetadata Metadata) {
587 if (auto ImportAs = Info.SwiftImportAs)
588 D->addAttr(SwiftAttrAttr::Create(S.Context, "import_" + ImportAs.value()));
589
590 if (auto RetainOp = Info.SwiftRetainOp)
591 D->addAttr(SwiftAttrAttr::Create(S.Context, "retain:" + RetainOp.value()));
592
593 if (auto ReleaseOp = Info.SwiftReleaseOp)
594 D->addAttr(
595 SwiftAttrAttr::Create(S.Context, "release:" + ReleaseOp.value()));
596
597 if (auto Extensibility = Info.EnumExtensibility) {
598 using api_notes::EnumExtensibilityKind;
599 bool ShouldAddAttribute = (*Extensibility != EnumExtensibilityKind::None);
600 handleAPINotedAttribute<EnumExtensibilityAttr>(
601 S, D, ShouldAddAttribute, Metadata, [&] {
602 EnumExtensibilityAttr::Kind kind;
603 switch (*Extensibility) {
604 case EnumExtensibilityKind::None:
605 llvm_unreachable("remove only");
606 case EnumExtensibilityKind::Open:
607 kind = EnumExtensibilityAttr::Open;
608 break;
609 case EnumExtensibilityKind::Closed:
610 kind = EnumExtensibilityAttr::Closed;
611 break;
612 }
613 return new (S.Context)
614 EnumExtensibilityAttr(S.Context, getPlaceholderAttrInfo(), kind);
615 });
616 }
617
618 if (auto FlagEnum = Info.isFlagEnum()) {
619 handleAPINotedAttribute<FlagEnumAttr>(S, D, *FlagEnum, Metadata, [&] {
620 return new (S.Context) FlagEnumAttr(S.Context, getPlaceholderAttrInfo());
621 });
622 }
623
624 // Handle common type information.
625 ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(Info),
626 Metadata);
627}
628
629/// Process API notes for a typedef.
630static void ProcessAPINotes(Sema &S, TypedefNameDecl *D,
631 const api_notes::TypedefInfo &Info,
632 VersionedInfoMetadata Metadata) {
633 // swift_wrapper
634 using SwiftWrapperKind = api_notes::SwiftNewTypeKind;
635
636 if (auto SwiftWrapper = Info.SwiftWrapper) {
637 handleAPINotedAttribute<SwiftNewTypeAttr>(
638 S, D, *SwiftWrapper != SwiftWrapperKind::None, Metadata, [&] {
639 SwiftNewTypeAttr::NewtypeKind Kind;
640 switch (*SwiftWrapper) {
641 case SwiftWrapperKind::None:
642 llvm_unreachable("Shouldn't build an attribute");
643
644 case SwiftWrapperKind::Struct:
645 Kind = SwiftNewTypeAttr::NK_Struct;
646 break;
647
648 case SwiftWrapperKind::Enum:
649 Kind = SwiftNewTypeAttr::NK_Enum;
650 break;
651 }
652 AttributeCommonInfo SyntaxInfo{
653 SourceRange(),
654 AttributeCommonInfo::AT_SwiftNewType,
655 {AttributeCommonInfo::AS_GNU, SwiftNewTypeAttr::GNU_swift_wrapper,
656 /*IsAlignas*/ false, /*IsRegularKeywordAttribute*/ false}};
657 return new (S.Context) SwiftNewTypeAttr(S.Context, SyntaxInfo, Kind);
658 });
659 }
660
661 // Handle common type information.
662 ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(Info),
663 Metadata);
664}
665
666/// Process API notes for an Objective-C class or protocol.
667static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D,
668 const api_notes::ObjCContextInfo &Info,
669 VersionedInfoMetadata Metadata) {
670 // Handle common type information.
671 ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(Info),
672 Metadata);
673}
674
675/// Process API notes for an Objective-C class.
676static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D,
677 const api_notes::ObjCContextInfo &Info,
678 VersionedInfoMetadata Metadata) {
679 if (auto AsNonGeneric = Info.getSwiftImportAsNonGeneric()) {
680 handleAPINotedAttribute<SwiftImportAsNonGenericAttr>(
681 S, D, *AsNonGeneric, Metadata, [&] {
682 return new (S.Context)
683 SwiftImportAsNonGenericAttr(S.Context, getPlaceholderAttrInfo());
684 });
685 }
686
687 if (auto ObjcMembers = Info.getSwiftObjCMembers()) {
688 handleAPINotedAttribute<SwiftObjCMembersAttr>(
689 S, D, *ObjcMembers, Metadata, [&] {
690 return new (S.Context)
691 SwiftObjCMembersAttr(S.Context, getPlaceholderAttrInfo());
692 });
693 }
694
695 // Handle information common to Objective-C classes and protocols.
696 ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), Info,
697 Metadata);
698}
699
700/// If we're applying API notes with an active, non-default version, and the
701/// versioned API notes have a SwiftName but the declaration normally wouldn't
702/// have one, add a removal attribute to make it clear that the new SwiftName
703/// attribute only applies to the active version of \p D, not to all versions.
704///
705/// This must be run \em before processing API notes for \p D, because otherwise
706/// any existing SwiftName attribute will have been packaged up in a
707/// SwiftVersionedAdditionAttr.
708template <typename SpecificInfo>
709static void maybeAttachUnversionedSwiftName(
710 Sema &S, Decl *D,
711 const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
712 if (D->hasAttr<SwiftNameAttr>())
713 return;
714 if (!Info.getSelected())
715 return;
716
717 // Is the active slice versioned, and does it set a Swift name?
718 VersionTuple SelectedVersion;
719 SpecificInfo SelectedInfoSlice;
720 std::tie(SelectedVersion, SelectedInfoSlice) = Info[*Info.getSelected()];
721 if (SelectedVersion.empty())
722 return;
723 if (SelectedInfoSlice.SwiftName.empty())
724 return;
725
726 // Does the unversioned slice /not/ set a Swift name?
727 for (const auto &VersionAndInfoSlice : Info) {
728 if (!VersionAndInfoSlice.first.empty())
729 continue;
730 if (!VersionAndInfoSlice.second.SwiftName.empty())
731 return;
732 }
733
734 // Then explicitly call that out with a removal attribute.
735 VersionedInfoMetadata DummyFutureMetadata(
736 SelectedVersion, IsActive_t::Inactive, IsSubstitution_t::Replacement);
737 handleAPINotedAttribute<SwiftNameAttr>(
738 S, D, /*add*/ false, DummyFutureMetadata, []() -> SwiftNameAttr * {
739 llvm_unreachable("should not try to add an attribute here");
740 });
741}
742
743/// Processes all versions of versioned API notes.
744///
745/// Just dispatches to the various ProcessAPINotes functions in this file.
746template <typename SpecificDecl, typename SpecificInfo>
747static void ProcessVersionedAPINotes(
748 Sema &S, SpecificDecl *D,
749 const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
750
751 maybeAttachUnversionedSwiftName(S, D, Info);
752
753 unsigned Selected = Info.getSelected().value_or(Info.size());
754
755 VersionTuple Version;
756 SpecificInfo InfoSlice;
757 for (unsigned i = 0, e = Info.size(); i != e; ++i) {
758 std::tie(Version, InfoSlice) = Info[i];
759 auto Active = (i == Selected) ? IsActive_t::Active : IsActive_t::Inactive;
760 auto Replacement = IsSubstitution_t::Original;
761 if (Active == IsActive_t::Inactive && Version.empty()) {
762 Replacement = IsSubstitution_t::Replacement;
763 Version = Info[Selected].first;
764 }
765 ProcessAPINotes(S, D, InfoSlice,
766 VersionedInfoMetadata(Version, Active, Replacement));
767 }
768}
769
770/// Process API notes that are associated with this declaration, mapping them
771/// to attributes as appropriate.
772void Sema::ProcessAPINotes(Decl *D) {
773 if (!D)
774 return;
775
776 // Globals.
777 if (D->getDeclContext()->isFileContext() ||
778 D->getDeclContext()->isNamespace() ||
779 D->getDeclContext()->isExternCContext() ||
780 D->getDeclContext()->isExternCXXContext()) {
781 std::optional<api_notes::Context> APINotesContext;
782 if (auto NamespaceContext = dyn_cast<NamespaceDecl>(Val: D->getDeclContext())) {
783 for (auto Reader :
784 APINotes.findAPINotes(NamespaceContext->getLocation())) {
785 // Retrieve the context ID for the parent namespace of the decl.
786 std::stack<NamespaceDecl *> NamespaceStack;
787 {
788 for (auto CurrentNamespace = NamespaceContext; CurrentNamespace;
789 CurrentNamespace =
790 dyn_cast<NamespaceDecl>(CurrentNamespace->getParent())) {
791 if (!CurrentNamespace->isInlineNamespace())
792 NamespaceStack.push(CurrentNamespace);
793 }
794 }
795 std::optional<api_notes::ContextID> NamespaceID;
796 while (!NamespaceStack.empty()) {
797 auto CurrentNamespace = NamespaceStack.top();
798 NamespaceStack.pop();
799 NamespaceID = Reader->lookupNamespaceID(CurrentNamespace->getName(),
800 NamespaceID);
801 if (!NamespaceID)
802 break;
803 }
804 if (NamespaceID)
805 APINotesContext = api_notes::Context(
806 *NamespaceID, api_notes::ContextKind::Namespace);
807 }
808 }
809
810 // Global variables.
811 if (auto VD = dyn_cast<VarDecl>(Val: D)) {
812 for (auto Reader : APINotes.findAPINotes(Loc: D->getLocation())) {
813 auto Info =
814 Reader->lookupGlobalVariable(Name: VD->getName(), Ctx: APINotesContext);
815 ProcessVersionedAPINotes(*this, VD, Info);
816 }
817
818 return;
819 }
820
821 // Global functions.
822 if (auto FD = dyn_cast<FunctionDecl>(Val: D)) {
823 if (FD->getDeclName().isIdentifier()) {
824 for (auto Reader : APINotes.findAPINotes(Loc: D->getLocation())) {
825 auto Info =
826 Reader->lookupGlobalFunction(Name: FD->getName(), Ctx: APINotesContext);
827 ProcessVersionedAPINotes(*this, FD, Info);
828 }
829 }
830
831 return;
832 }
833
834 // Objective-C classes.
835 if (auto Class = dyn_cast<ObjCInterfaceDecl>(Val: D)) {
836 for (auto Reader : APINotes.findAPINotes(Loc: D->getLocation())) {
837 auto Info = Reader->lookupObjCClassInfo(Name: Class->getName());
838 ProcessVersionedAPINotes(*this, Class, Info);
839 }
840
841 return;
842 }
843
844 // Objective-C protocols.
845 if (auto Protocol = dyn_cast<ObjCProtocolDecl>(Val: D)) {
846 for (auto Reader : APINotes.findAPINotes(Loc: D->getLocation())) {
847 auto Info = Reader->lookupObjCProtocolInfo(Name: Protocol->getName());
848 ProcessVersionedAPINotes(*this, Protocol, Info);
849 }
850
851 return;
852 }
853
854 // Tags
855 if (auto Tag = dyn_cast<TagDecl>(Val: D)) {
856 std::string LookupName = Tag->getName().str();
857
858 // Use the source location to discern if this Tag is an OPTIONS macro.
859 // For now we would like to limit this trick of looking up the APINote tag
860 // using the EnumDecl's QualType in the case where the enum is anonymous.
861 // This is only being used to support APINotes lookup for C++
862 // NS/CF_OPTIONS when C++-Interop is enabled.
863 std::string MacroName =
864 LookupName.empty() && Tag->getOuterLocStart().isMacroID()
865 ? clang::Lexer::getImmediateMacroName(
866 Loc: Tag->getOuterLocStart(),
867 SM: Tag->getASTContext().getSourceManager(), LangOpts)
868 .str()
869 : "";
870
871 if (LookupName.empty() && isa<clang::EnumDecl>(Val: Tag) &&
872 (MacroName == "CF_OPTIONS" || MacroName == "NS_OPTIONS" ||
873 MacroName == "OBJC_OPTIONS" || MacroName == "SWIFT_OPTIONS")) {
874
875 clang::QualType T = llvm::cast<clang::EnumDecl>(Val: Tag)->getIntegerType();
876 LookupName = clang::QualType::getAsString(
877 split: T.split(), Policy: getASTContext().getPrintingPolicy());
878 }
879
880 for (auto Reader : APINotes.findAPINotes(Loc: D->getLocation())) {
881 auto Info = Reader->lookupTag(Name: LookupName, Ctx: APINotesContext);
882 ProcessVersionedAPINotes(*this, Tag, Info);
883 }
884
885 return;
886 }
887
888 // Typedefs
889 if (auto Typedef = dyn_cast<TypedefNameDecl>(Val: D)) {
890 for (auto Reader : APINotes.findAPINotes(Loc: D->getLocation())) {
891 auto Info = Reader->lookupTypedef(Name: Typedef->getName(), Ctx: APINotesContext);
892 ProcessVersionedAPINotes(*this, Typedef, Info);
893 }
894
895 return;
896 }
897 }
898
899 // Enumerators.
900 if (D->getDeclContext()->getRedeclContext()->isFileContext() ||
901 D->getDeclContext()->getRedeclContext()->isExternCContext()) {
902 if (auto EnumConstant = dyn_cast<EnumConstantDecl>(Val: D)) {
903 for (auto Reader : APINotes.findAPINotes(Loc: D->getLocation())) {
904 auto Info = Reader->lookupEnumConstant(Name: EnumConstant->getName());
905 ProcessVersionedAPINotes(*this, EnumConstant, Info);
906 }
907
908 return;
909 }
910 }
911
912 if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(Val: D->getDeclContext())) {
913 // Location function that looks up an Objective-C context.
914 auto GetContext = [&](api_notes::APINotesReader *Reader)
915 -> std::optional<api_notes::ContextID> {
916 if (auto Protocol = dyn_cast<ObjCProtocolDecl>(Val: ObjCContainer)) {
917 if (auto Found = Reader->lookupObjCProtocolID(Protocol->getName()))
918 return *Found;
919
920 return std::nullopt;
921 }
922
923 if (auto Impl = dyn_cast<ObjCCategoryImplDecl>(Val: ObjCContainer)) {
924 if (auto Cat = Impl->getCategoryDecl())
925 ObjCContainer = Cat->getClassInterface();
926 else
927 return std::nullopt;
928 }
929
930 if (auto Category = dyn_cast<ObjCCategoryDecl>(Val: ObjCContainer)) {
931 if (Category->getClassInterface())
932 ObjCContainer = Category->getClassInterface();
933 else
934 return std::nullopt;
935 }
936
937 if (auto Impl = dyn_cast<ObjCImplDecl>(Val: ObjCContainer)) {
938 if (Impl->getClassInterface())
939 ObjCContainer = Impl->getClassInterface();
940 else
941 return std::nullopt;
942 }
943
944 if (auto Class = dyn_cast<ObjCInterfaceDecl>(Val: ObjCContainer)) {
945 if (auto Found = Reader->lookupObjCClassID(Class->getName()))
946 return *Found;
947
948 return std::nullopt;
949 }
950
951 return std::nullopt;
952 };
953
954 // Objective-C methods.
955 if (auto Method = dyn_cast<ObjCMethodDecl>(Val: D)) {
956 for (auto Reader : APINotes.findAPINotes(Loc: D->getLocation())) {
957 if (auto Context = GetContext(Reader)) {
958 // Map the selector.
959 Selector Sel = Method->getSelector();
960 SmallVector<StringRef, 2> SelPieces;
961 if (Sel.isUnarySelector()) {
962 SelPieces.push_back(Elt: Sel.getNameForSlot(argIndex: 0));
963 } else {
964 for (unsigned i = 0, n = Sel.getNumArgs(); i != n; ++i)
965 SelPieces.push_back(Elt: Sel.getNameForSlot(argIndex: i));
966 }
967
968 api_notes::ObjCSelectorRef SelectorRef;
969 SelectorRef.NumArgs = Sel.getNumArgs();
970 SelectorRef.Identifiers = SelPieces;
971
972 auto Info = Reader->lookupObjCMethod(CtxID: *Context, Selector: SelectorRef,
973 IsInstanceMethod: Method->isInstanceMethod());
974 ProcessVersionedAPINotes(S&: *this, D: Method, Info);
975 }
976 }
977 }
978
979 // Objective-C properties.
980 if (auto Property = dyn_cast<ObjCPropertyDecl>(Val: D)) {
981 for (auto Reader : APINotes.findAPINotes(Loc: D->getLocation())) {
982 if (auto Context = GetContext(Reader)) {
983 bool isInstanceProperty =
984 (Property->getPropertyAttributesAsWritten() &
985 ObjCPropertyAttribute::kind_class) == 0;
986 auto Info = Reader->lookupObjCProperty(CtxID: *Context, Name: Property->getName(),
987 IsInstance: isInstanceProperty);
988 ProcessVersionedAPINotes(*this, Property, Info);
989 }
990 }
991
992 return;
993 }
994 }
995}
996

source code of clang/lib/Sema/SemaAPINotes.cpp