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