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