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