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