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