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 "SemaAPINotesInternal.h"
14#include "TypeLocBuilder.h"
15#include "clang/APINotes/APINotesReader.h"
16#include "clang/APINotes/Types.h"
17#include "clang/AST/Decl.h"
18#include "clang/AST/DeclCXX.h"
19#include "clang/AST/DeclObjC.h"
20#include "clang/AST/TypeLoc.h"
21#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
22#include "clang/Basic/SourceLocation.h"
23#include "clang/Lex/Lexer.h"
24#include "clang/Sema/SemaObjC.h"
25#include "clang/Sema/SemaSwift.h"
26#include <stack>
27
28using namespace clang;
29
30namespace {
31enum class IsActive_t : bool { Inactive, Active };
32enum class IsSubstitution_t : bool { Original, Replacement };
33
34struct VersionedInfoMetadata {
35 /// An empty version refers to unversioned metadata.
36 VersionTuple Version;
37 unsigned IsActive : 1;
38 unsigned IsReplacement : 1;
39
40 VersionedInfoMetadata(VersionTuple Version, IsActive_t Active,
41 IsSubstitution_t Replacement)
42 : Version(Version), IsActive(Active == IsActive_t::Active),
43 IsReplacement(Replacement == IsSubstitution_t::Replacement) {}
44};
45} // end anonymous namespace
46
47/// Determine whether this is a multi-level pointer type.
48static bool isIndirectPointerType(QualType Type) {
49 QualType Pointee = Type->getPointeeType();
50 if (Pointee.isNull())
51 return false;
52
53 return Pointee->isAnyPointerType() || Pointee->isObjCObjectPointerType() ||
54 Pointee->isMemberPointerType();
55}
56
57static void applyAPINotesType(Sema &S, Decl *decl, StringRef typeString,
58 VersionedInfoMetadata metadata) {
59 if (typeString.empty())
60
61 return;
62
63 // Version-independent APINotes add "type" annotations
64 // with a versioned attribute for the client to select and apply.
65 if (S.captureSwiftVersionIndependentAPINotes()) {
66 auto *typeAttr = SwiftTypeAttr::CreateImplicit(Ctx&: S.Context, TypeString: typeString);
67 auto *versioned = SwiftVersionedAdditionAttr::CreateImplicit(
68 Ctx&: S.Context, Version: metadata.Version, AdditionalAttr: typeAttr, IsReplacedByActive: metadata.IsReplacement);
69 decl->addAttr(A: versioned);
70 } else {
71 if (!metadata.IsActive)
72 return;
73 S.ApplyAPINotesType(D: decl, TypeString: typeString);
74 }
75}
76
77/// Apply nullability to the given declaration.
78static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability,
79 VersionedInfoMetadata metadata) {
80 // Version-independent APINotes add "nullability" annotations
81 // with a versioned attribute for the client to select and apply.
82 if (S.captureSwiftVersionIndependentAPINotes()) {
83 SwiftNullabilityAttr::Kind attrNullabilityKind;
84 switch (nullability) {
85 case NullabilityKind::NonNull:
86 attrNullabilityKind = SwiftNullabilityAttr::Kind::NonNull;
87 break;
88 case NullabilityKind::Nullable:
89 attrNullabilityKind = SwiftNullabilityAttr::Kind::Nullable;
90 break;
91 case NullabilityKind::Unspecified:
92 attrNullabilityKind = SwiftNullabilityAttr::Kind::Unspecified;
93 break;
94 case NullabilityKind::NullableResult:
95 attrNullabilityKind = SwiftNullabilityAttr::Kind::NullableResult;
96 break;
97 }
98 auto *nullabilityAttr =
99 SwiftNullabilityAttr::CreateImplicit(Ctx&: S.Context, Kind: attrNullabilityKind);
100 auto *versioned = SwiftVersionedAdditionAttr::CreateImplicit(
101 Ctx&: S.Context, Version: metadata.Version, AdditionalAttr: nullabilityAttr, IsReplacedByActive: metadata.IsReplacement);
102 decl->addAttr(A: versioned);
103 return;
104 } else {
105 if (!metadata.IsActive)
106 return;
107
108 S.ApplyNullability(D: decl, Nullability: nullability);
109 }
110}
111
112/// Copy a string into ASTContext-allocated memory.
113static StringRef ASTAllocateString(ASTContext &Ctx, StringRef String) {
114 void *mem = Ctx.Allocate(Size: String.size(), Align: alignof(char *));
115 memcpy(dest: mem, src: String.data(), n: String.size());
116 return StringRef(static_cast<char *>(mem), String.size());
117}
118
119static AttributeCommonInfo getPlaceholderAttrInfo() {
120 return AttributeCommonInfo(SourceRange(),
121 AttributeCommonInfo::UnknownAttribute,
122 {AttributeCommonInfo::AS_GNU,
123 /*Spelling*/ 0, /*IsAlignas*/ false,
124 /*IsRegularKeywordAttribute*/ false});
125}
126
127namespace {
128template <typename A> struct AttrKindFor {};
129
130#define ATTR(X) \
131 template <> struct AttrKindFor<X##Attr> { \
132 static const attr::Kind value = attr::X; \
133 };
134#include "clang/Basic/AttrList.inc"
135
136/// Handle an attribute introduced by API notes.
137///
138/// \param IsAddition Whether we should add a new attribute
139/// (otherwise, we might remove an existing attribute).
140/// \param CreateAttr Create the new attribute to be added.
141template <typename A>
142void handleAPINotedAttribute(
143 Sema &S, Decl *D, bool IsAddition, VersionedInfoMetadata Metadata,
144 llvm::function_ref<A *()> CreateAttr,
145 llvm::function_ref<Decl::attr_iterator(const Decl *)> GetExistingAttr) {
146 if (Metadata.IsActive) {
147 auto Existing = GetExistingAttr(D);
148 if (Existing != D->attr_end()) {
149 // Remove the existing attribute, and treat it as a superseded
150 // non-versioned attribute.
151 auto *Versioned = SwiftVersionedAdditionAttr::CreateImplicit(
152 Ctx&: S.Context, Version: Metadata.Version, AdditionalAttr: *Existing, /*IsReplacedByActive*/ true);
153
154 D->getAttrs().erase(CI: Existing);
155 D->addAttr(A: Versioned);
156 }
157
158 // If we're supposed to add a new attribute, do so.
159 if (IsAddition) {
160 if (auto Attr = CreateAttr())
161 D->addAttr(A: Attr);
162 }
163
164 return;
165 }
166 if (IsAddition) {
167 if (auto Attr = CreateAttr()) {
168 auto *Versioned = SwiftVersionedAdditionAttr::CreateImplicit(
169 S.Context, Metadata.Version, Attr,
170 /*IsReplacedByActive*/ Metadata.IsReplacement);
171 D->addAttr(A: Versioned);
172 }
173 } else {
174 // FIXME: This isn't preserving enough information for things like
175 // availability, where we're trying to remove a /specific/ kind of
176 // attribute.
177 auto *Versioned = SwiftVersionedRemovalAttr::CreateImplicit(
178 S.Context, Metadata.Version, AttrKindFor<A>::value,
179 /*IsReplacedByActive*/ Metadata.IsReplacement);
180 D->addAttr(A: Versioned);
181 }
182}
183
184template <typename A>
185void handleAPINotedAttribute(Sema &S, Decl *D, bool ShouldAddAttribute,
186 VersionedInfoMetadata Metadata,
187 llvm::function_ref<A *()> CreateAttr) {
188 handleAPINotedAttribute<A>(
189 S, D, ShouldAddAttribute, Metadata, CreateAttr, [](const Decl *D) {
190 return llvm::find_if(D->attrs(),
191 [](const Attr *Next) { return isa<A>(Next); });
192 });
193}
194} // namespace
195
196template <typename A>
197static void handleAPINotedRetainCountAttribute(Sema &S, Decl *D,
198 bool ShouldAddAttribute,
199 VersionedInfoMetadata Metadata) {
200 // The template argument has a default to make the "removal" case more
201 // concise; it doesn't matter /which/ attribute is being removed.
202 handleAPINotedAttribute<A>(
203 S, D, ShouldAddAttribute, Metadata,
204 [&] { return new (S.Context) A(S.Context, getPlaceholderAttrInfo()); },
205 [](const Decl *D) -> Decl::attr_iterator {
206 return llvm::find_if(D->attrs(), [](const Attr *Next) -> bool {
207 return isa<CFReturnsRetainedAttr>(Val: Next) ||
208 isa<CFReturnsNotRetainedAttr>(Val: Next) ||
209 isa<NSReturnsRetainedAttr>(Val: Next) ||
210 isa<NSReturnsNotRetainedAttr>(Val: Next) ||
211 isa<CFAuditedTransferAttr>(Val: Next);
212 });
213 });
214}
215
216static void handleAPINotedRetainCountConvention(
217 Sema &S, Decl *D, VersionedInfoMetadata Metadata,
218 std::optional<api_notes::RetainCountConventionKind> Convention) {
219 if (!Convention)
220 return;
221 switch (*Convention) {
222 case api_notes::RetainCountConventionKind::None:
223 if (isa<FunctionDecl>(Val: D)) {
224 handleAPINotedRetainCountAttribute<CFUnknownTransferAttr>(
225 S, D, /*shouldAddAttribute*/ ShouldAddAttribute: true, Metadata);
226 } else {
227 handleAPINotedRetainCountAttribute<CFReturnsRetainedAttr>(
228 S, D, /*shouldAddAttribute*/ ShouldAddAttribute: false, Metadata);
229 }
230 break;
231 case api_notes::RetainCountConventionKind::CFReturnsRetained:
232 handleAPINotedRetainCountAttribute<CFReturnsRetainedAttr>(
233 S, D, /*shouldAddAttribute*/ ShouldAddAttribute: true, Metadata);
234 break;
235 case api_notes::RetainCountConventionKind::CFReturnsNotRetained:
236 handleAPINotedRetainCountAttribute<CFReturnsNotRetainedAttr>(
237 S, D, /*shouldAddAttribute*/ ShouldAddAttribute: true, Metadata);
238 break;
239 case api_notes::RetainCountConventionKind::NSReturnsRetained:
240 handleAPINotedRetainCountAttribute<NSReturnsRetainedAttr>(
241 S, D, /*shouldAddAttribute*/ ShouldAddAttribute: true, Metadata);
242 break;
243 case api_notes::RetainCountConventionKind::NSReturnsNotRetained:
244 handleAPINotedRetainCountAttribute<NSReturnsNotRetainedAttr>(
245 S, D, /*shouldAddAttribute*/ ShouldAddAttribute: true, Metadata);
246 break;
247 }
248}
249
250/// Add a 'swift_attr' unless \p D already carries that exact annotation.
251static void addSwiftAttrIfAbsent(Sema &S, Decl *D, StringRef Attribute) {
252 for (const auto *A : D->specific_attrs<SwiftAttrAttr>())
253 if (A->getAttribute() == Attribute)
254 return;
255
256 D->addAttr(A: SwiftAttrAttr::Create(Ctx&: S.Context, Attribute));
257}
258
259static void ProcessAPINotes(Sema &S, Decl *D,
260 const api_notes::CommonEntityInfo &Info,
261 VersionedInfoMetadata Metadata) {
262 // Availability
263 if (Info.Unavailable) {
264 handleAPINotedAttribute<UnavailableAttr>(S, D, ShouldAddAttribute: true, Metadata, CreateAttr: [&] {
265 return new (S.Context)
266 UnavailableAttr(S.Context, getPlaceholderAttrInfo(),
267 ASTAllocateString(Ctx&: S.Context, String: Info.UnavailableMsg));
268 });
269 }
270
271 if (Info.UnavailableInSwift) {
272 handleAPINotedAttribute<AvailabilityAttr>(
273 S, D, IsAddition: true, Metadata,
274 CreateAttr: [&] {
275 return new (S.Context) AvailabilityAttr(
276 S.Context, getPlaceholderAttrInfo(),
277 &S.Context.Idents.get(Name: "swift"), VersionTuple(), VersionTuple(),
278 VersionTuple(),
279 /*Unavailable=*/true,
280 ASTAllocateString(Ctx&: S.Context, String: Info.UnavailableMsg),
281 /*Strict=*/false,
282 /*Replacement=*/StringRef(),
283 /*Priority=*/Sema::AP_Explicit,
284 /*Environment=*/nullptr);
285 },
286 GetExistingAttr: [](const Decl *D) {
287 return llvm::find_if(Range: D->attrs(), P: [](const Attr *next) -> bool {
288 if (const auto *AA = dyn_cast<AvailabilityAttr>(Val: next))
289 if (const auto *II = AA->getPlatform())
290 return II->isStr(Str: "swift");
291 return false;
292 });
293 });
294 }
295
296 // swift_private
297 if (auto SwiftPrivate = Info.isSwiftPrivate()) {
298 handleAPINotedAttribute<SwiftPrivateAttr>(
299 S, D, ShouldAddAttribute: *SwiftPrivate, Metadata, CreateAttr: [&] {
300 return new (S.Context)
301 SwiftPrivateAttr(S.Context, getPlaceholderAttrInfo());
302 });
303 }
304
305 // swift_safety
306 if (auto SafetyKind = Info.getSwiftSafety()) {
307 bool Addition = *SafetyKind != api_notes::SwiftSafetyKind::Unspecified;
308 handleAPINotedAttribute<SwiftAttrAttr>(
309 S, D, IsAddition: Addition, Metadata,
310 CreateAttr: [&] {
311 return SwiftAttrAttr::Create(
312 Ctx&: S.Context, Attribute: *SafetyKind == api_notes::SwiftSafetyKind::Safe
313 ? "safe"
314 : "unsafe");
315 },
316 GetExistingAttr: [](const Decl *D) {
317 return llvm::find_if(Range: D->attrs(), P: [](const Attr *attr) {
318 if (const auto *swiftAttr = dyn_cast<SwiftAttrAttr>(Val: attr)) {
319 if (swiftAttr->getAttribute() == "safe" ||
320 swiftAttr->getAttribute() == "unsafe")
321 return true;
322 }
323 return false;
324 });
325 });
326 }
327
328 // swift_name
329 if (!Info.SwiftName.empty()) {
330 handleAPINotedAttribute<SwiftNameAttr>(
331 S, D, ShouldAddAttribute: true, Metadata, CreateAttr: [&]() -> SwiftNameAttr * {
332 AttributeFactory AF{};
333 AttributePool AP{AF};
334 auto &C = S.getASTContext();
335 ParsedAttr *SNA = AP.create(
336 attrName: &C.Idents.get(Name: "swift_name"), attrRange: SourceRange(), scope: AttributeScopeInfo(),
337 Param1: nullptr, Param2: nullptr, Param3: nullptr, form: ParsedAttr::Form::GNU());
338
339 if (!S.Swift().DiagnoseName(D, Name: Info.SwiftName, Loc: D->getLocation(), AL: *SNA,
340 /*IsAsync=*/false))
341 return nullptr;
342
343 return new (S.Context)
344 SwiftNameAttr(S.Context, getPlaceholderAttrInfo(),
345 ASTAllocateString(Ctx&: S.Context, String: Info.SwiftName));
346 });
347 }
348}
349
350static void ProcessAPINotes(Sema &S, Decl *D,
351 const api_notes::CommonTypeInfo &Info,
352 VersionedInfoMetadata Metadata) {
353 // swift_bridge
354 if (auto SwiftBridge = Info.getSwiftBridge()) {
355 handleAPINotedAttribute<SwiftBridgeAttr>(
356 S, D, ShouldAddAttribute: !SwiftBridge->empty(), Metadata, CreateAttr: [&] {
357 return new (S.Context)
358 SwiftBridgeAttr(S.Context, getPlaceholderAttrInfo(),
359 ASTAllocateString(Ctx&: S.Context, String: *SwiftBridge));
360 });
361 }
362
363 // ns_error_domain
364 if (auto NSErrorDomain = Info.getNSErrorDomain()) {
365 handleAPINotedAttribute<NSErrorDomainAttr>(
366 S, D, ShouldAddAttribute: !NSErrorDomain->empty(), Metadata, CreateAttr: [&] {
367 return new (S.Context)
368 NSErrorDomainAttr(S.Context, getPlaceholderAttrInfo(),
369 &S.Context.Idents.get(Name: *NSErrorDomain));
370 });
371 }
372
373 if (auto ConformsTo = Info.getSwiftConformance())
374 addSwiftAttrIfAbsent(S, D, Attribute: "conforms_to:" + ConformsTo.value());
375
376 ProcessAPINotes(S, D, Info: static_cast<const api_notes::CommonEntityInfo &>(Info),
377 Metadata);
378}
379
380/// Check that the replacement type provided by API notes is reasonable.
381///
382/// This is a very weak form of ABI check.
383static bool checkAPINotesReplacementType(Sema &S, SourceLocation Loc,
384 QualType OrigType,
385 QualType ReplacementType) {
386 if (S.Context.getTypeSize(T: OrigType) !=
387 S.Context.getTypeSize(T: ReplacementType)) {
388 S.Diag(Loc, DiagID: diag::err_incompatible_replacement_type)
389 << ReplacementType << OrigType;
390 return true;
391 }
392
393 return false;
394}
395
396void Sema::ApplyAPINotesType(Decl *D, StringRef TypeString) {
397 if (!TypeString.empty() && ParseTypeFromStringCallback) {
398 auto ParsedType = ParseTypeFromStringCallback(TypeString, "<API Notes>",
399 D->getLocation());
400 if (ParsedType.isUsable()) {
401 QualType Type = Sema::GetTypeFromParser(Ty: ParsedType.get());
402 auto TypeInfo = Context.getTrivialTypeSourceInfo(T: Type, Loc: D->getLocation());
403 if (auto Var = dyn_cast<VarDecl>(Val: D)) {
404 // Make adjustments to parameter types.
405 if (isa<ParmVarDecl>(Val: Var)) {
406 Type = ObjC().AdjustParameterTypeForObjCAutoRefCount(
407 T: Type, NameLoc: D->getLocation(), TSInfo: TypeInfo);
408 Type = Context.getAdjustedParameterType(T: Type);
409 }
410
411 if (!checkAPINotesReplacementType(S&: *this, Loc: Var->getLocation(),
412 OrigType: Var->getType(), ReplacementType: Type)) {
413 Var->setType(Type);
414 Var->setTypeSourceInfo(TypeInfo);
415 }
416 } else if (auto property = dyn_cast<ObjCPropertyDecl>(Val: D)) {
417 if (!checkAPINotesReplacementType(S&: *this, Loc: property->getLocation(),
418 OrigType: property->getType(), ReplacementType: Type)) {
419 property->setType(T: Type, TSI: TypeInfo);
420 }
421 } else if (auto field = dyn_cast<FieldDecl>(Val: D)) {
422 if (!checkAPINotesReplacementType(S&: *this, Loc: field->getLocation(),
423 OrigType: field->getType(), ReplacementType: Type)) {
424 field->setType(Type);
425 field->setTypeSourceInfo(TypeInfo);
426 }
427 } else {
428 llvm_unreachable("API notes allowed a type on an unknown declaration");
429 }
430 }
431 }
432}
433
434void Sema::ApplyNullability(Decl *D, NullabilityKind Nullability) {
435 auto GetModified =
436 [&](class Decl *D, QualType QT,
437 NullabilityKind Nullability) -> std::optional<QualType> {
438 QualType Original = QT;
439 CheckImplicitNullabilityTypeSpecifier(Type&: QT, Nullability, DiagLoc: D->getLocation(),
440 AllowArrayTypes: isa<ParmVarDecl>(Val: D),
441 /*OverrideExisting=*/true);
442 return (QT.getTypePtr() != Original.getTypePtr()) ? std::optional(QT)
443 : std::nullopt;
444 };
445
446 if (auto Function = dyn_cast<FunctionDecl>(Val: D)) {
447 if (auto Modified =
448 GetModified(D, Function->getReturnType(), Nullability)) {
449 const FunctionType *FnType = Function->getType()->castAs<FunctionType>();
450 if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(Val: FnType))
451 Function->setType(Context.getFunctionType(
452 ResultTy: *Modified, Args: proto->getParamTypes(), EPI: proto->getExtProtoInfo()));
453 else
454 Function->setType(
455 Context.getFunctionNoProtoType(ResultTy: *Modified, Info: FnType->getExtInfo()));
456 }
457 } else if (auto Method = dyn_cast<ObjCMethodDecl>(Val: D)) {
458 if (auto Modified = GetModified(D, Method->getReturnType(), Nullability)) {
459 Method->setReturnType(*Modified);
460
461 // Make it a context-sensitive keyword if we can.
462 if (!isIndirectPointerType(Type: *Modified))
463 Method->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
464 Method->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
465 }
466 } else if (auto Value = dyn_cast<ValueDecl>(Val: D)) {
467 if (auto Modified = GetModified(D, Value->getType(), Nullability)) {
468 Value->setType(*Modified);
469
470 // Make it a context-sensitive keyword if we can.
471 if (auto Parm = dyn_cast<ParmVarDecl>(Val: D)) {
472 if (Parm->isObjCMethodParameter() && !isIndirectPointerType(Type: *Modified))
473 Parm->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
474 Parm->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
475 }
476 }
477 } else if (auto Property = dyn_cast<ObjCPropertyDecl>(Val: D)) {
478 if (auto Modified = GetModified(D, Property->getType(), Nullability)) {
479 Property->setType(T: *Modified, TSI: Property->getTypeSourceInfo());
480
481 // Make it a property attribute if we can.
482 if (!isIndirectPointerType(Type: *Modified))
483 Property->setPropertyAttributes(
484 ObjCPropertyAttribute::kind_null_resettable);
485 }
486 }
487}
488
489/// Process API notes for a variable or property.
490static void ProcessAPINotes(Sema &S, Decl *D,
491 const api_notes::VariableInfo &Info,
492 VersionedInfoMetadata Metadata) {
493 // Type override.
494 applyAPINotesType(S, decl: D, typeString: Info.getType(), metadata: Metadata);
495
496 // Nullability.
497 if (auto Nullability = Info.getNullability())
498 applyNullability(S, decl: D, nullability: *Nullability, metadata: Metadata);
499
500 // Handle common entity information.
501 ProcessAPINotes(S, D, Info: static_cast<const api_notes::CommonEntityInfo &>(Info),
502 Metadata);
503}
504
505/// Process API notes for a parameter.
506static void ProcessAPINotes(Sema &S, ParmVarDecl *D,
507 const api_notes::ParamInfo &Info,
508 VersionedInfoMetadata Metadata) {
509 // noescape
510 if (auto NoEscape = Info.isNoEscape())
511 handleAPINotedAttribute<NoEscapeAttr>(S, D, ShouldAddAttribute: *NoEscape, Metadata, CreateAttr: [&] {
512 return new (S.Context) NoEscapeAttr(S.Context, getPlaceholderAttrInfo());
513 });
514
515 if (auto Lifetimebound = Info.isLifetimebound())
516 handleAPINotedAttribute<LifetimeBoundAttr>(
517 S, D, ShouldAddAttribute: *Lifetimebound, Metadata, CreateAttr: [&] {
518 return new (S.Context)
519 LifetimeBoundAttr(S.Context, getPlaceholderAttrInfo());
520 });
521
522 // Retain count convention
523 handleAPINotedRetainCountConvention(S, D, Metadata,
524 Convention: Info.getRetainCountConvention());
525
526 // Handle common entity information.
527 ProcessAPINotes(S, D, Info: static_cast<const api_notes::VariableInfo &>(Info),
528 Metadata);
529}
530
531/// Process API notes for a global variable.
532static void ProcessAPINotes(Sema &S, VarDecl *D,
533 const api_notes::GlobalVariableInfo &Info,
534 VersionedInfoMetadata metadata) {
535 // Handle common entity information.
536 ProcessAPINotes(S, D, Info: static_cast<const api_notes::VariableInfo &>(Info),
537 Metadata: metadata);
538}
539
540/// Process API notes for a C field.
541static void ProcessAPINotes(Sema &S, FieldDecl *D,
542 const api_notes::FieldInfo &Info,
543 VersionedInfoMetadata metadata) {
544 // Handle common entity information.
545 ProcessAPINotes(S, D, Info: static_cast<const api_notes::VariableInfo &>(Info),
546 Metadata: metadata);
547}
548
549/// Process API notes for an Objective-C property.
550static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D,
551 const api_notes::ObjCPropertyInfo &Info,
552 VersionedInfoMetadata Metadata) {
553 // Handle common entity information.
554 ProcessAPINotes(S, D, Info: static_cast<const api_notes::VariableInfo &>(Info),
555 Metadata);
556
557 if (auto AsAccessors = Info.getSwiftImportAsAccessors()) {
558 handleAPINotedAttribute<SwiftImportPropertyAsAccessorsAttr>(
559 S, D, ShouldAddAttribute: *AsAccessors, Metadata, CreateAttr: [&] {
560 return new (S.Context) SwiftImportPropertyAsAccessorsAttr(
561 S.Context, getPlaceholderAttrInfo());
562 });
563 }
564}
565
566namespace {
567typedef llvm::PointerUnion<FunctionDecl *, ObjCMethodDecl *> FunctionOrMethod;
568}
569
570/// Process API notes for a function or method.
571static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc,
572 const api_notes::FunctionInfo &Info,
573 VersionedInfoMetadata Metadata) {
574 // Find the declaration itself.
575 FunctionDecl *FD = dyn_cast<FunctionDecl *>(Val&: AnyFunc);
576 Decl *D = FD;
577 ObjCMethodDecl *MD = nullptr;
578 if (!D) {
579 MD = cast<ObjCMethodDecl *>(Val&: AnyFunc);
580 D = MD;
581 }
582
583 assert((FD || MD) && "Expecting Function or ObjCMethod");
584
585 // Nullability of return type.
586 if (Info.NullabilityAudited)
587 applyNullability(S, decl: D, nullability: Info.getReturnTypeInfo(), metadata: Metadata);
588
589 // Add [[clang::unsafe_buffer_usage]]
590 if (Info.UnsafeBufferUsage && !D->getAttr<UnsafeBufferUsageAttr>()) {
591 handleAPINotedAttribute<UnsafeBufferUsageAttr>(S, D, ShouldAddAttribute: true, Metadata, CreateAttr: [&]() {
592 return UnsafeBufferUsageAttr::Create(Ctx&: S.getASTContext(),
593 CommonInfo: getPlaceholderAttrInfo());
594 });
595 }
596
597 // Parameters.
598 unsigned NumParams = FD ? FD->getNumParams() : MD->param_size();
599
600 bool AnyTypeChanged = false;
601 for (unsigned I = 0; I != NumParams; ++I) {
602 ParmVarDecl *Param = FD ? FD->getParamDecl(i: I) : MD->param_begin()[I];
603 QualType ParamTypeBefore = Param->getType();
604
605 if (I < Info.Params.size())
606 ProcessAPINotes(S, D: Param, Info: Info.Params[I], Metadata);
607
608 // Nullability.
609 if (Info.NullabilityAudited)
610 applyNullability(S, decl: Param, nullability: Info.getParamTypeInfo(index: I), metadata: Metadata);
611
612 if (ParamTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr())
613 AnyTypeChanged = true;
614 }
615
616 // returns_(un)retained
617 if (!Info.SwiftReturnOwnership.empty())
618 addSwiftAttrIfAbsent(S, D, Attribute: "returns_" + Info.SwiftReturnOwnership);
619
620 // Result type override.
621 QualType OverriddenResultType;
622 if (Metadata.IsActive && !Info.ResultType.empty() &&
623 S.ParseTypeFromStringCallback) {
624 auto ParsedType = S.ParseTypeFromStringCallback(
625 Info.ResultType, "<API Notes>", D->getLocation());
626 if (ParsedType.isUsable()) {
627 QualType ResultType = Sema::GetTypeFromParser(Ty: ParsedType.get());
628
629 if (MD) {
630 if (!checkAPINotesReplacementType(S, Loc: D->getLocation(),
631 OrigType: MD->getReturnType(), ReplacementType: ResultType)) {
632 auto ResultTypeInfo =
633 S.Context.getTrivialTypeSourceInfo(T: ResultType, Loc: D->getLocation());
634 MD->setReturnType(ResultType);
635 MD->setReturnTypeSourceInfo(ResultTypeInfo);
636 }
637 } else if (!checkAPINotesReplacementType(
638 S, Loc: FD->getLocation(), OrigType: FD->getReturnType(), ReplacementType: ResultType)) {
639 OverriddenResultType = ResultType;
640 AnyTypeChanged = true;
641 }
642 }
643 }
644
645 // If the result type or any of the parameter types changed for a function
646 // declaration, we have to rebuild the type.
647 if (FD && AnyTypeChanged) {
648 if (const auto *fnProtoType = FD->getType()->getAs<FunctionProtoType>()) {
649 if (OverriddenResultType.isNull())
650 OverriddenResultType = fnProtoType->getReturnType();
651
652 SmallVector<QualType, 4> ParamTypes;
653 for (auto Param : FD->parameters())
654 ParamTypes.push_back(Elt: Param->getType());
655
656 FD->setType(S.Context.getFunctionType(ResultTy: OverriddenResultType, Args: ParamTypes,
657 EPI: fnProtoType->getExtProtoInfo()));
658 } else if (!OverriddenResultType.isNull()) {
659 const auto *FnNoProtoType = FD->getType()->castAs<FunctionNoProtoType>();
660 FD->setType(S.Context.getFunctionNoProtoType(
661 ResultTy: OverriddenResultType, Info: FnNoProtoType->getExtInfo()));
662 }
663 }
664
665 // Retain count convention
666 handleAPINotedRetainCountConvention(S, D, Metadata,
667 Convention: Info.getRetainCountConvention());
668
669 // Handle common entity information.
670 ProcessAPINotes(S, D, Info: static_cast<const api_notes::CommonEntityInfo &>(Info),
671 Metadata);
672}
673
674/// Process API notes for a C++ method.
675static void ProcessAPINotes(Sema &S, CXXMethodDecl *Method,
676 const api_notes::CXXMethodInfo &Info,
677 VersionedInfoMetadata Metadata) {
678 if (Info.This && Info.This->isLifetimebound() &&
679 !lifetimes::implicitObjectParamIsLifetimeBound(FD: Method)) {
680 auto MethodType = Method->getType();
681 auto *attr = ::new (S.Context)
682 LifetimeBoundAttr(S.Context, getPlaceholderAttrInfo());
683 QualType AttributedType =
684 S.Context.getAttributedType(attr, modifiedType: MethodType, equivalentType: MethodType);
685 TypeLocBuilder TLB;
686 TLB.pushFullCopy(L: Method->getTypeSourceInfo()->getTypeLoc());
687 AttributedTypeLoc TyLoc = TLB.push<AttributedTypeLoc>(T: AttributedType);
688 TyLoc.setAttr(attr);
689 Method->setType(AttributedType);
690 Method->setTypeSourceInfo(TLB.getTypeSourceInfo(Context&: S.Context, T: AttributedType));
691 }
692
693 ProcessAPINotes(S, AnyFunc: (FunctionOrMethod)Method, Info, Metadata);
694}
695
696/// Process API notes for a global function.
697static void ProcessAPINotes(Sema &S, FunctionDecl *D,
698 const api_notes::GlobalFunctionInfo &Info,
699 VersionedInfoMetadata Metadata) {
700 // Handle common function information.
701 ProcessAPINotes(S, AnyFunc: FunctionOrMethod(D),
702 Info: static_cast<const api_notes::FunctionInfo &>(Info), Metadata);
703}
704
705/// Process API notes for an enumerator.
706static void ProcessAPINotes(Sema &S, EnumConstantDecl *D,
707 const api_notes::EnumConstantInfo &Info,
708 VersionedInfoMetadata Metadata) {
709 // Handle common information.
710 ProcessAPINotes(S, D, Info: static_cast<const api_notes::CommonEntityInfo &>(Info),
711 Metadata);
712}
713
714/// Process API notes for an Objective-C method.
715static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D,
716 const api_notes::ObjCMethodInfo &Info,
717 VersionedInfoMetadata Metadata) {
718 // Designated initializers.
719 if (Info.DesignatedInit) {
720 handleAPINotedAttribute<ObjCDesignatedInitializerAttr>(
721 S, D, ShouldAddAttribute: true, Metadata, CreateAttr: [&] {
722 if (ObjCInterfaceDecl *IFace = D->getClassInterface())
723 IFace->setHasDesignatedInitializers();
724
725 return new (S.Context) ObjCDesignatedInitializerAttr(
726 S.Context, getPlaceholderAttrInfo());
727 });
728 }
729
730 // Handle common function information.
731 ProcessAPINotes(S, AnyFunc: FunctionOrMethod(D),
732 Info: static_cast<const api_notes::FunctionInfo &>(Info), Metadata);
733}
734
735/// Process API notes for a tag.
736static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &Info,
737 VersionedInfoMetadata Metadata) {
738 if (auto ImportAs = Info.SwiftImportAs)
739 addSwiftAttrIfAbsent(S, D, Attribute: "import_" + ImportAs.value());
740
741 if (auto RetainOp = Info.SwiftRetainOp)
742 addSwiftAttrIfAbsent(S, D, Attribute: "retain:" + RetainOp.value());
743
744 if (auto ReleaseOp = Info.SwiftReleaseOp)
745 addSwiftAttrIfAbsent(S, D, Attribute: "release:" + ReleaseOp.value());
746 if (auto DestroyOp = Info.SwiftDestroyOp)
747 addSwiftAttrIfAbsent(S, D, Attribute: "destroy:" + DestroyOp.value());
748 if (auto DefaultOwnership = Info.SwiftDefaultOwnership)
749 addSwiftAttrIfAbsent(
750 S, D, Attribute: "returned_as_" + DefaultOwnership.value() + "_by_default");
751
752 if (auto Copyable = Info.isSwiftCopyable()) {
753 if (!*Copyable)
754 addSwiftAttrIfAbsent(S, D, Attribute: "~Copyable");
755 }
756
757 if (auto Escapable = Info.isSwiftEscapable()) {
758 addSwiftAttrIfAbsent(S, D, Attribute: *Escapable ? "Escapable" : "~Escapable");
759 }
760
761 if (auto Extensibility = Info.EnumExtensibility) {
762 using api_notes::EnumExtensibilityKind;
763 bool ShouldAddAttribute = (*Extensibility != EnumExtensibilityKind::None);
764 handleAPINotedAttribute<EnumExtensibilityAttr>(
765 S, D, ShouldAddAttribute, Metadata, CreateAttr: [&] {
766 EnumExtensibilityAttr::Kind kind;
767 switch (*Extensibility) {
768 case EnumExtensibilityKind::None:
769 llvm_unreachable("remove only");
770 case EnumExtensibilityKind::Open:
771 kind = EnumExtensibilityAttr::Open;
772 break;
773 case EnumExtensibilityKind::Closed:
774 kind = EnumExtensibilityAttr::Closed;
775 break;
776 }
777 return new (S.Context)
778 EnumExtensibilityAttr(S.Context, getPlaceholderAttrInfo(), kind);
779 });
780 }
781
782 if (auto FlagEnum = Info.isFlagEnum()) {
783 handleAPINotedAttribute<FlagEnumAttr>(S, D, ShouldAddAttribute: *FlagEnum, Metadata, CreateAttr: [&] {
784 return new (S.Context) FlagEnumAttr(S.Context, getPlaceholderAttrInfo());
785 });
786 }
787
788 // Handle common type information.
789 ProcessAPINotes(S, D, Info: static_cast<const api_notes::CommonTypeInfo &>(Info),
790 Metadata);
791}
792
793/// Process API notes for a typedef.
794static void ProcessAPINotes(Sema &S, TypedefNameDecl *D,
795 const api_notes::TypedefInfo &Info,
796 VersionedInfoMetadata Metadata) {
797 // swift_wrapper
798 using SwiftWrapperKind = api_notes::SwiftNewTypeKind;
799
800 if (auto SwiftWrapper = Info.SwiftWrapper) {
801 handleAPINotedAttribute<SwiftNewTypeAttr>(
802 S, D, ShouldAddAttribute: *SwiftWrapper != SwiftWrapperKind::None, Metadata, CreateAttr: [&] {
803 SwiftNewTypeAttr::NewtypeKind Kind;
804 switch (*SwiftWrapper) {
805 case SwiftWrapperKind::None:
806 llvm_unreachable("Shouldn't build an attribute");
807
808 case SwiftWrapperKind::Struct:
809 Kind = SwiftNewTypeAttr::NK_Struct;
810 break;
811
812 case SwiftWrapperKind::Enum:
813 Kind = SwiftNewTypeAttr::NK_Enum;
814 break;
815 }
816 AttributeCommonInfo SyntaxInfo{
817 SourceRange(),
818 AttributeCommonInfo::AT_SwiftNewType,
819 {AttributeCommonInfo::AS_GNU, SwiftNewTypeAttr::GNU_swift_wrapper,
820 /*IsAlignas*/ false, /*IsRegularKeywordAttribute*/ false}};
821 return new (S.Context) SwiftNewTypeAttr(S.Context, SyntaxInfo, Kind);
822 });
823 }
824
825 // Handle common type information.
826 ProcessAPINotes(S, D, Info: static_cast<const api_notes::CommonTypeInfo &>(Info),
827 Metadata);
828}
829
830/// Process API notes for an Objective-C class or protocol.
831static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D,
832 const api_notes::ContextInfo &Info,
833 VersionedInfoMetadata Metadata) {
834 // Handle common type information.
835 ProcessAPINotes(S, D, Info: static_cast<const api_notes::CommonTypeInfo &>(Info),
836 Metadata);
837}
838
839/// Process API notes for an Objective-C class.
840static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D,
841 const api_notes::ContextInfo &Info,
842 VersionedInfoMetadata Metadata) {
843 if (auto AsNonGeneric = Info.getSwiftImportAsNonGeneric()) {
844 handleAPINotedAttribute<SwiftImportAsNonGenericAttr>(
845 S, D, ShouldAddAttribute: *AsNonGeneric, Metadata, CreateAttr: [&] {
846 return new (S.Context)
847 SwiftImportAsNonGenericAttr(S.Context, getPlaceholderAttrInfo());
848 });
849 }
850
851 if (auto ObjcMembers = Info.getSwiftObjCMembers()) {
852 handleAPINotedAttribute<SwiftObjCMembersAttr>(
853 S, D, ShouldAddAttribute: *ObjcMembers, Metadata, CreateAttr: [&] {
854 return new (S.Context)
855 SwiftObjCMembersAttr(S.Context, getPlaceholderAttrInfo());
856 });
857 }
858
859 // Handle information common to Objective-C classes and protocols.
860 ProcessAPINotes(S, D: static_cast<clang::ObjCContainerDecl *>(D), Info,
861 Metadata);
862}
863
864/// If we're applying API notes with an active, non-default version, and the
865/// versioned API notes have a SwiftName but the declaration normally wouldn't
866/// have one, add a removal attribute to make it clear that the new SwiftName
867/// attribute only applies to the active version of \p D, not to all versions.
868///
869/// This must be run \em before processing API notes for \p D, because otherwise
870/// any existing SwiftName attribute will have been packaged up in a
871/// SwiftVersionedAdditionAttr.
872template <typename SpecificInfo>
873static void maybeAttachUnversionedSwiftName(
874 Sema &S, Decl *D,
875 const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
876 if (D->hasAttr<SwiftNameAttr>())
877 return;
878 if (!Info.getSelected())
879 return;
880
881 // Is the active slice versioned, and does it set a Swift name?
882 VersionTuple SelectedVersion;
883 SpecificInfo SelectedInfoSlice;
884 std::tie(SelectedVersion, SelectedInfoSlice) = Info[*Info.getSelected()];
885 if (SelectedVersion.empty())
886 return;
887 if (SelectedInfoSlice.SwiftName.empty())
888 return;
889
890 // Does the unversioned slice /not/ set a Swift name?
891 for (const auto &VersionAndInfoSlice : Info) {
892 if (!VersionAndInfoSlice.first.empty())
893 continue;
894 if (!VersionAndInfoSlice.second.SwiftName.empty())
895 return;
896 }
897
898 // Then explicitly call that out with a removal attribute.
899 VersionedInfoMetadata DummyFutureMetadata(
900 SelectedVersion, IsActive_t::Inactive, IsSubstitution_t::Replacement);
901 handleAPINotedAttribute<SwiftNameAttr>(
902 S, D, /*add*/ false, DummyFutureMetadata, []() -> SwiftNameAttr * {
903 llvm_unreachable("should not try to add an attribute here");
904 });
905}
906
907/// Processes all versions of versioned API notes.
908///
909/// Just dispatches to the various ProcessAPINotes functions in this file.
910template <typename SpecificDecl, typename SpecificInfo>
911static void ProcessVersionedAPINotes(
912 Sema &S, SpecificDecl *D,
913 const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
914
915 if (!S.captureSwiftVersionIndependentAPINotes())
916 maybeAttachUnversionedSwiftName(S, D, Info);
917
918 unsigned Selected = Info.getSelected().value_or(Info.size());
919
920 VersionTuple Version;
921 SpecificInfo InfoSlice;
922 for (unsigned i = 0, e = Info.size(); i != e; ++i) {
923 std::tie(Version, InfoSlice) = Info[i];
924 auto Active = (i == Selected) ? IsActive_t::Active : IsActive_t::Inactive;
925 auto Replacement = IsSubstitution_t::Original;
926
927 // When collecting all APINotes as version-independent,
928 // capture all as inactive and defer to the client to select the
929 // right one.
930 if (S.captureSwiftVersionIndependentAPINotes()) {
931 Active = IsActive_t::Inactive;
932 Replacement = IsSubstitution_t::Original;
933 } else if (Active == IsActive_t::Inactive && Version.empty()) {
934 Replacement = IsSubstitution_t::Replacement;
935 Version = Info[Selected].first;
936 }
937
938 ProcessAPINotes(S, D, InfoSlice,
939 VersionedInfoMetadata(Version, Active, Replacement));
940 }
941}
942
943static std::optional<api_notes::Context>
944UnwindNamespaceContext(DeclContext *DC, api_notes::APINotesManager &APINotes) {
945 if (auto NamespaceContext = dyn_cast<NamespaceDecl>(Val: DC)) {
946 for (auto Reader : APINotes.findAPINotes(Loc: NamespaceContext->getLocation())) {
947 // Retrieve the context ID for the parent namespace of the decl.
948 std::stack<NamespaceDecl *> NamespaceStack;
949 {
950 for (auto CurrentNamespace = NamespaceContext; CurrentNamespace;
951 CurrentNamespace =
952 dyn_cast<NamespaceDecl>(Val: CurrentNamespace->getParent())) {
953 if (!CurrentNamespace->isInlineNamespace())
954 NamespaceStack.push(x: CurrentNamespace);
955 }
956 }
957 std::optional<api_notes::ContextID> NamespaceID;
958 while (!NamespaceStack.empty()) {
959 auto CurrentNamespace = NamespaceStack.top();
960 NamespaceStack.pop();
961 NamespaceID =
962 Reader->lookupNamespaceID(Name: CurrentNamespace->getName(), ParentNamespaceID: NamespaceID);
963 if (!NamespaceID)
964 return std::nullopt;
965 }
966 if (NamespaceID)
967 return api_notes::Context(*NamespaceID,
968 api_notes::ContextKind::Namespace);
969 }
970 }
971 return std::nullopt;
972}
973
974static std::optional<api_notes::Context>
975UnwindTagContext(TagDecl *DC, api_notes::APINotesManager &APINotes) {
976 assert(DC && "tag context must not be null");
977 for (auto Reader : APINotes.findAPINotes(Loc: DC->getLocation())) {
978 // Retrieve the context ID for the parent tag of the decl.
979 std::stack<TagDecl *> TagStack;
980 {
981 for (auto CurrentTag = DC; CurrentTag;
982 CurrentTag = dyn_cast<TagDecl>(Val: CurrentTag->getParent()))
983 TagStack.push(x: CurrentTag);
984 }
985 assert(!TagStack.empty());
986 std::optional<api_notes::Context> Ctx =
987 UnwindNamespaceContext(DC: TagStack.top()->getDeclContext(), APINotes);
988 while (!TagStack.empty()) {
989 auto CurrentTag = TagStack.top();
990 TagStack.pop();
991 auto CtxID = Reader->lookupTagID(Name: CurrentTag->getName(), ParentCtx: Ctx);
992 if (!CtxID)
993 return std::nullopt;
994 Ctx = api_notes::Context(*CtxID, api_notes::ContextKind::Tag);
995 }
996 return Ctx;
997 }
998 return std::nullopt;
999}
1000
1001namespace clang {
1002struct APINotesParameterSelector {
1003 SmallVector<std::string, 4> Parameters;
1004
1005 bool operator==(const APINotesParameterSelector &Other) const {
1006 return Parameters == Other.Parameters;
1007 }
1008
1009 bool operator!=(const APINotesParameterSelector &Other) const {
1010 return !(*this == Other);
1011 }
1012};
1013
1014struct APINotesParameterSelectorCandidates {
1015 APINotesParameterSelector Source;
1016 std::optional<APINotesParameterSelector> Desugared;
1017};
1018} // namespace clang
1019
1020static PrintingPolicy
1021getAPINotesParameterSelectorPrintingPolicy(const ASTContext &Context) {
1022 PrintingPolicy Policy(Context.getLangOpts());
1023 Policy.PrintAsCanonical = false;
1024 Policy.FullyQualifiedName = false;
1025 Policy.SuppressScope = false;
1026 Policy.UsePreferredNames = false;
1027 Policy.MSVCFormatting = false;
1028 Policy.SplitTemplateClosers = false;
1029 Policy.IncludeNewlines = false;
1030 return Policy;
1031}
1032
1033// Print the APINotes selector spelling for one parameter. The source-spelled
1034// selector is tried first. The desugared spelling is only a permissive
1035// fallback.
1036static std::string getAPINotesParameterSelectorSpelling(
1037 QualType ParamType, const ASTContext &Context, const PrintingPolicy &Policy,
1038 bool Desugar) {
1039 if (Desugar)
1040 ParamType = ParamType.getDesugaredType(Context);
1041
1042 ParamType.removeLocalConst();
1043 ParamType.removeLocalVolatile();
1044 ParamType = ParamType.stripNullability(ctx: Context);
1045
1046 return ParamType.getAsString(Policy);
1047}
1048
1049static std::optional<APINotesParameterSelectorCandidates>
1050getAPINotesParameterSelectorCandidates(const Sema &S, const FunctionDecl *FD) {
1051 const auto *FPT = FD->getType()->getAs<FunctionProtoType>();
1052 if (!FPT)
1053 return std::nullopt;
1054
1055 APINotesParameterSelectorCandidates Candidates;
1056 APINotesParameterSelector Desugared;
1057 Candidates.Source.Parameters.reserve(N: FPT->getNumParams());
1058 Desugared.Parameters.reserve(N: FPT->getNumParams());
1059
1060 const PrintingPolicy Policy =
1061 getAPINotesParameterSelectorPrintingPolicy(Context: S.Context);
1062 for (QualType ParamType : FPT->param_types()) {
1063 Candidates.Source.Parameters.push_back(
1064 Elt: getAPINotesParameterSelectorSpelling(ParamType, Context: S.Context, Policy,
1065 /*Desugar=*/false));
1066 Desugared.Parameters.push_back(Elt: getAPINotesParameterSelectorSpelling(
1067 ParamType, Context: S.Context, Policy, /*Desugar=*/true));
1068 }
1069
1070 if (Candidates.Source != Desugared)
1071 Candidates.Desugared = std::move(Desugared);
1072
1073 return Candidates;
1074}
1075
1076APINotesSelectorDiagnosticReaderState &
1077APINotesSelectorDiagnosticState::getOrCreateReaderState(
1078 api_notes::APINotesReader &Reader) {
1079 auto [StateIt, Inserted] = Readers.try_emplace(Key: &Reader);
1080 APINotesSelectorDiagnosticReaderState &State = StateIt->second;
1081 if (!Inserted)
1082 return State;
1083
1084 SmallVector<api_notes::APINotesFunctionSelectorKey, 4> Selectors;
1085 Reader.collectExactFunctionParameterSelectors(Selectors);
1086 State.addSelectors(Selectors);
1087 return State;
1088}
1089
1090static APINotesSelectorDiagnosticReaderState &
1091getAPINotesSelectorDiagnosticState(Sema &S, api_notes::APINotesReader *Reader) {
1092 if (!S.APINotesSelectorDiagnostics)
1093 S.APINotesSelectorDiagnostics =
1094 std::make_unique<APINotesSelectorDiagnosticState>();
1095
1096 return S.APINotesSelectorDiagnostics->getOrCreateReaderState(Reader&: *Reader);
1097}
1098
1099void APINotesSelectorDiagnosticReaderState::markCandidatesUsed(
1100 llvm::function_ref<std::optional<api_notes::APINotesFunctionSelectorKey>(
1101 ArrayRef<std::string>)>
1102 GetSelectorKey,
1103 const APINotesParameterSelectorCandidates &Candidates) {
1104 if (auto Key = GetSelectorKey(Candidates.Source.Parameters))
1105 markUsed(Key: *Key);
1106 if (Candidates.Desugared) {
1107 if (auto Key = GetSelectorKey(Candidates.Desugared->Parameters))
1108 markUsed(Key: *Key);
1109 }
1110}
1111
1112// Apply the first exact selector entry found. This preserves source-spelling
1113// precedence over the desugared fallback and avoids applying multiple exact
1114// entries for the same declaration.
1115template <typename SpecificInfo, typename SpecificDecl>
1116static void processExactAPINotes(
1117 Sema &S, SpecificDecl *D,
1118 const APINotesParameterSelectorCandidates &ParameterSelectorCandidates,
1119 llvm::function_ref<api_notes::APINotesReader::VersionedInfo<SpecificInfo>(
1120 ArrayRef<std::string>)>
1121 LookupExact) {
1122 auto ProcessSelector = [&](const APINotesParameterSelector &Selector) {
1123 auto Info = LookupExact(Selector.Parameters);
1124 if (Info.size() == 0)
1125 return false;
1126
1127 ProcessVersionedAPINotes(S, D, Info);
1128 return true;
1129 };
1130
1131 if (ProcessSelector(ParameterSelectorCandidates.Source))
1132 return;
1133
1134 if (ParameterSelectorCandidates.Desugared)
1135 ProcessSelector(*ParameterSelectorCandidates.Desugared);
1136}
1137
1138/// Process API notes that are associated with this declaration, mapping them
1139/// to attributes as appropriate.
1140void Sema::ProcessAPINotes(Decl *D) {
1141 if (!D)
1142 return;
1143 if (!APINotes.hasAPINotes())
1144 return;
1145 auto Readers = APINotes.findAPINotes(Loc: D->getLocation());
1146 if (Readers.empty())
1147 return;
1148
1149 auto *DC = D->getDeclContext();
1150 // Globals.
1151 if (DC->isFileContext() || DC->isNamespace() ||
1152 DC->getDeclKind() == Decl::LinkageSpec) {
1153 std::optional<api_notes::Context> APINotesContext =
1154 UnwindNamespaceContext(DC, APINotes);
1155 // Global variables.
1156 if (auto VD = dyn_cast<VarDecl>(Val: D)) {
1157 for (auto Reader : Readers) {
1158 auto Info =
1159 Reader->lookupGlobalVariable(Name: VD->getName(), Ctx: APINotesContext);
1160 ProcessVersionedAPINotes(S&: *this, D: VD, Info);
1161 }
1162
1163 return;
1164 }
1165
1166 // Global functions.
1167 if (auto FD = dyn_cast<FunctionDecl>(Val: D)) {
1168 if (FD->getDeclName().isIdentifier()) {
1169 auto ParameterSelectorCandidates =
1170 getAPINotesParameterSelectorCandidates(S: *this, FD);
1171
1172 for (auto Reader : Readers) {
1173 auto Info =
1174 Reader->lookupGlobalFunction(Name: FD->getName(), Ctx: APINotesContext);
1175 ProcessVersionedAPINotes(S&: *this, D: FD, Info);
1176
1177 if (ParameterSelectorCandidates)
1178 processExactAPINotes<api_notes::GlobalFunctionInfo>(
1179 S&: *this, D: FD, ParameterSelectorCandidates: *ParameterSelectorCandidates,
1180 LookupExact: [&](ArrayRef<std::string> Parameters) {
1181 return Reader->lookupGlobalFunction(Name: FD->getName(), Parameters,
1182 Ctx: APINotesContext);
1183 });
1184
1185 if (ParameterSelectorCandidates) {
1186 auto &DiagnosticState =
1187 getAPINotesSelectorDiagnosticState(S&: *this, Reader);
1188 if (auto BroadKey = Reader->getGlobalFunctionSelectorKey(
1189 Name: FD->getName(), Ctx: APINotesContext))
1190 DiagnosticState.noteSeenDeclaration(Key: *BroadKey, Name: FD->getName(),
1191 Loc: FD->getLocation());
1192 DiagnosticState.markCandidatesUsed(
1193 GetSelectorKey: [&](ArrayRef<std::string> Parameters) {
1194 return Reader->getGlobalFunctionSelectorKey(
1195 Name: FD->getName(), Parameters, Ctx: APINotesContext);
1196 },
1197 Candidates: *ParameterSelectorCandidates);
1198 }
1199 }
1200 }
1201
1202 return;
1203 }
1204
1205 // Objective-C classes.
1206 if (auto Class = dyn_cast<ObjCInterfaceDecl>(Val: D)) {
1207 for (auto Reader : Readers) {
1208 auto Info = Reader->lookupObjCClassInfo(Name: Class->getName());
1209 ProcessVersionedAPINotes(S&: *this, D: Class, Info);
1210 }
1211
1212 return;
1213 }
1214
1215 // Objective-C protocols.
1216 if (auto Protocol = dyn_cast<ObjCProtocolDecl>(Val: D)) {
1217 for (auto Reader : Readers) {
1218 auto Info = Reader->lookupObjCProtocolInfo(Name: Protocol->getName());
1219 ProcessVersionedAPINotes(S&: *this, D: Protocol, Info);
1220 }
1221
1222 return;
1223 }
1224
1225 // Tags
1226 if (auto Tag = dyn_cast<TagDecl>(Val: D)) {
1227 // Determine the name of the entity to search for. If this is an
1228 // anonymous tag that gets its linked name from a typedef, look for the
1229 // typedef name. This allows tag-specific information to be added
1230 // to the declaration.
1231 std::string LookupName;
1232 if (auto typedefName = Tag->getTypedefNameForAnonDecl())
1233 LookupName = typedefName->getName().str();
1234 else
1235 LookupName = Tag->getName().str();
1236
1237 // Use the source location to discern if this Tag is an OPTIONS macro.
1238 // For now we would like to limit this trick of looking up the APINote tag
1239 // using the EnumDecl's QualType in the case where the enum is anonymous.
1240 // This is only being used to support APINotes lookup for C++
1241 // NS/CF_OPTIONS when C++-Interop is enabled.
1242 std::string MacroName =
1243 LookupName.empty() && Tag->getOuterLocStart().isMacroID()
1244 ? clang::Lexer::getImmediateMacroName(
1245 Loc: Tag->getOuterLocStart(),
1246 SM: Tag->getASTContext().getSourceManager(), LangOpts)
1247 .str()
1248 : "";
1249
1250 if (LookupName.empty() && isa<clang::EnumDecl>(Val: Tag) &&
1251 (MacroName == "CF_OPTIONS" || MacroName == "NS_OPTIONS" ||
1252 MacroName == "OBJC_OPTIONS" || MacroName == "SWIFT_OPTIONS")) {
1253
1254 clang::QualType T = llvm::cast<clang::EnumDecl>(Val: Tag)->getIntegerType();
1255 LookupName = clang::QualType::getAsString(
1256 split: T.split(), Policy: getASTContext().getPrintingPolicy());
1257 }
1258
1259 for (auto Reader : Readers) {
1260 if (auto ParentTag = dyn_cast<TagDecl>(Val: Tag->getDeclContext()))
1261 APINotesContext = UnwindTagContext(DC: ParentTag, APINotes);
1262 auto Info = Reader->lookupTag(Name: LookupName, Ctx: APINotesContext);
1263 ProcessVersionedAPINotes(S&: *this, D: Tag, Info);
1264 }
1265
1266 return;
1267 }
1268
1269 // Typedefs
1270 if (auto Typedef = dyn_cast<TypedefNameDecl>(Val: D)) {
1271 for (auto Reader : Readers) {
1272 auto Info = Reader->lookupTypedef(Name: Typedef->getName(), Ctx: APINotesContext);
1273 ProcessVersionedAPINotes(S&: *this, D: Typedef, Info);
1274 }
1275
1276 return;
1277 }
1278 }
1279
1280 // Enumerators.
1281 if (DC->getRedeclContext()->isFileContext() ||
1282 DC->getRedeclContext()->isExternCContext()) {
1283 if (auto EnumConstant = dyn_cast<EnumConstantDecl>(Val: D)) {
1284 for (auto Reader : Readers) {
1285 auto Info = Reader->lookupEnumConstant(Name: EnumConstant->getName());
1286 ProcessVersionedAPINotes(S&: *this, D: EnumConstant, Info);
1287 }
1288
1289 return;
1290 }
1291 }
1292
1293 if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(Val: DC)) {
1294 // Location function that looks up an Objective-C context.
1295 auto GetContext = [&](api_notes::APINotesReader *Reader)
1296 -> std::optional<api_notes::ContextID> {
1297 if (auto Protocol = dyn_cast<ObjCProtocolDecl>(Val: ObjCContainer)) {
1298 if (auto Found = Reader->lookupObjCProtocolID(Name: Protocol->getName()))
1299 return *Found;
1300
1301 return std::nullopt;
1302 }
1303
1304 if (auto Impl = dyn_cast<ObjCCategoryImplDecl>(Val: ObjCContainer)) {
1305 if (auto Cat = Impl->getCategoryDecl())
1306 ObjCContainer = Cat->getClassInterface();
1307 else
1308 return std::nullopt;
1309 }
1310
1311 if (auto Category = dyn_cast<ObjCCategoryDecl>(Val: ObjCContainer)) {
1312 if (Category->getClassInterface())
1313 ObjCContainer = Category->getClassInterface();
1314 else
1315 return std::nullopt;
1316 }
1317
1318 if (auto Impl = dyn_cast<ObjCImplDecl>(Val: ObjCContainer)) {
1319 if (Impl->getClassInterface())
1320 ObjCContainer = Impl->getClassInterface();
1321 else
1322 return std::nullopt;
1323 }
1324
1325 if (auto Class = dyn_cast<ObjCInterfaceDecl>(Val: ObjCContainer)) {
1326 if (auto Found = Reader->lookupObjCClassID(Name: Class->getName()))
1327 return *Found;
1328
1329 return std::nullopt;
1330 }
1331
1332 return std::nullopt;
1333 };
1334
1335 // Objective-C methods.
1336 if (auto Method = dyn_cast<ObjCMethodDecl>(Val: D)) {
1337 for (auto Reader : Readers) {
1338 if (auto Context = GetContext(Reader)) {
1339 // Map the selector.
1340 Selector Sel = Method->getSelector();
1341 SmallVector<StringRef, 2> SelPieces;
1342 if (Sel.isUnarySelector()) {
1343 SelPieces.push_back(Elt: Sel.getNameForSlot(argIndex: 0));
1344 } else {
1345 for (unsigned i = 0, n = Sel.getNumArgs(); i != n; ++i)
1346 SelPieces.push_back(Elt: Sel.getNameForSlot(argIndex: i));
1347 }
1348
1349 api_notes::ObjCSelectorRef SelectorRef;
1350 SelectorRef.NumArgs = Sel.getNumArgs();
1351 SelectorRef.Identifiers = SelPieces;
1352
1353 auto Info = Reader->lookupObjCMethod(CtxID: *Context, Selector: SelectorRef,
1354 IsInstanceMethod: Method->isInstanceMethod());
1355 ProcessVersionedAPINotes(S&: *this, D: Method, Info);
1356 }
1357 }
1358 }
1359
1360 // Objective-C properties.
1361 if (auto Property = dyn_cast<ObjCPropertyDecl>(Val: D)) {
1362 for (auto Reader : APINotes.findAPINotes(Loc: D->getLocation())) {
1363 if (auto Context = GetContext(Reader)) {
1364 bool isInstanceProperty =
1365 (Property->getPropertyAttributesAsWritten() &
1366 ObjCPropertyAttribute::kind_class) == 0;
1367 auto Info = Reader->lookupObjCProperty(CtxID: *Context, Name: Property->getName(),
1368 IsInstance: isInstanceProperty);
1369 ProcessVersionedAPINotes(S&: *this, D: Property, Info);
1370 }
1371 }
1372
1373 return;
1374 }
1375 }
1376
1377 if (auto TagContext = dyn_cast<TagDecl>(Val: DC)) {
1378 if (auto CXXMethod = dyn_cast<CXXMethodDecl>(Val: D)) {
1379 if (!isa<CXXConstructorDecl>(Val: CXXMethod) &&
1380 !isa<CXXDestructorDecl>(Val: CXXMethod) &&
1381 !isa<CXXConversionDecl>(Val: CXXMethod)) {
1382 auto ParameterSelectorCandidates =
1383 getAPINotesParameterSelectorCandidates(S: *this, FD: CXXMethod);
1384 for (auto Reader : Readers) {
1385 if (auto Context = UnwindTagContext(DC: TagContext, APINotes)) {
1386 std::string MethodName;
1387 if (CXXMethod->isOverloadedOperator())
1388 MethodName =
1389 std::string("operator") +
1390 getOperatorSpelling(Operator: CXXMethod->getOverloadedOperator());
1391 else
1392 MethodName = CXXMethod->getName();
1393
1394 auto Info = Reader->lookupCXXMethod(CtxID: Context->id, Name: MethodName);
1395 ProcessVersionedAPINotes(S&: *this, D: CXXMethod, Info);
1396
1397 if (ParameterSelectorCandidates)
1398 processExactAPINotes<api_notes::CXXMethodInfo>(
1399 S&: *this, D: CXXMethod, ParameterSelectorCandidates: *ParameterSelectorCandidates,
1400 LookupExact: [&](ArrayRef<std::string> Parameters) {
1401 return Reader->lookupCXXMethod(CtxID: Context->id, Name: MethodName,
1402 Parameters);
1403 });
1404
1405 if (ParameterSelectorCandidates) {
1406 auto &DiagnosticState =
1407 getAPINotesSelectorDiagnosticState(S&: *this, Reader);
1408 if (auto BroadKey =
1409 Reader->getCXXMethodSelectorKey(CtxID: Context->id, Name: MethodName))
1410 DiagnosticState.noteSeenDeclaration(Key: *BroadKey, Name: MethodName,
1411 Loc: CXXMethod->getLocation());
1412 DiagnosticState.markCandidatesUsed(
1413 GetSelectorKey: [&](ArrayRef<std::string> Parameters) {
1414 return Reader->getCXXMethodSelectorKey(
1415 CtxID: Context->id, Name: MethodName, Parameters);
1416 },
1417 Candidates: *ParameterSelectorCandidates);
1418 }
1419 }
1420 }
1421 }
1422 }
1423
1424 if (auto Field = dyn_cast<FieldDecl>(Val: D)) {
1425 if (!Field->isUnnamedBitField() && !Field->isAnonymousStructOrUnion()) {
1426 for (auto Reader : Readers) {
1427 if (auto Context = UnwindTagContext(DC: TagContext, APINotes)) {
1428 auto Info = Reader->lookupField(CtxID: Context->id, Name: Field->getName());
1429 ProcessVersionedAPINotes(S&: *this, D: Field, Info);
1430 }
1431 }
1432 }
1433 }
1434
1435 if (auto Tag = dyn_cast<TagDecl>(Val: D)) {
1436 for (auto Reader : Readers) {
1437 if (auto Context = UnwindTagContext(DC: TagContext, APINotes)) {
1438 auto Info = Reader->lookupTag(Name: Tag->getName(), Ctx: Context);
1439 ProcessVersionedAPINotes(S&: *this, D: Tag, Info);
1440 }
1441 }
1442 }
1443 }
1444}
1445
1446void APINotesSelectorDiagnosticReaderState::diagnoseUnused(
1447 Sema &S, api_notes::APINotesReader &Reader) const {
1448 for (const auto &Selector : SelectorUsed) {
1449 if (Selector.second)
1450 continue;
1451
1452 auto SeenName =
1453 SeenNames.find(Val: Selector.first.getWithoutParameterSelector());
1454 if (SeenName == SeenNames.end())
1455 continue;
1456
1457 std::optional<SmallVector<std::string, 4>> ParameterSpellings =
1458 Reader.getParameterSelectorSpellingsForDiagnostics(Key: Selector.first);
1459 if (!ParameterSpellings)
1460 continue;
1461
1462 S.Diag(Loc: SeenName->second.Loc, DiagID: diag::warn_apinotes_message)
1463 << (llvm::Twine("API notes entry for '") + SeenName->second.Name +
1464 "' has unmatched Where.Parameters " +
1465 api_notes::formatAPINotesParameterSelector(Parameters&: *ParameterSpellings))
1466 .str();
1467 }
1468}
1469
1470void APINotesSelectorDiagnosticState::diagnoseUnused(Sema &S) const {
1471 for (const auto &ReaderSelectors : Readers)
1472 ReaderSelectors.second.diagnoseUnused(S, Reader&: *ReaderSelectors.first);
1473}
1474
1475void Sema::DiagnoseUnusedAPINotesSelectors() {
1476 if (!APINotesSelectorDiagnostics)
1477 return;
1478
1479 if (!Diags.isIgnored(DiagID: diag::warn_apinotes_message, Loc: SourceLocation()))
1480 APINotesSelectorDiagnostics->diagnoseUnused(S&: *this);
1481 APINotesSelectorDiagnostics.reset();
1482}
1483