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