1 | //===--- SemaAvailability.cpp - Availability attribute 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 processes the availability attribute. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/AST/Attr.h" |
14 | #include "clang/AST/Decl.h" |
15 | #include "clang/AST/DeclTemplate.h" |
16 | #include "clang/AST/DynamicRecursiveASTVisitor.h" |
17 | #include "clang/AST/ExprObjC.h" |
18 | #include "clang/AST/StmtObjC.h" |
19 | #include "clang/Basic/DiagnosticSema.h" |
20 | #include "clang/Basic/IdentifierTable.h" |
21 | #include "clang/Basic/LangOptions.h" |
22 | #include "clang/Basic/TargetInfo.h" |
23 | #include "clang/Lex/Preprocessor.h" |
24 | #include "clang/Sema/DelayedDiagnostic.h" |
25 | #include "clang/Sema/ScopeInfo.h" |
26 | #include "clang/Sema/Sema.h" |
27 | #include "clang/Sema/SemaObjC.h" |
28 | #include "llvm/ADT/StringRef.h" |
29 | #include <optional> |
30 | |
31 | using namespace clang; |
32 | using namespace sema; |
33 | |
34 | static bool hasMatchingEnvironmentOrNone(const ASTContext &Context, |
35 | const AvailabilityAttr *AA) { |
36 | IdentifierInfo *IIEnvironment = AA->getEnvironment(); |
37 | auto Environment = Context.getTargetInfo().getTriple().getEnvironment(); |
38 | if (!IIEnvironment || Environment == llvm::Triple::UnknownEnvironment) |
39 | return true; |
40 | |
41 | llvm::Triple::EnvironmentType ET = |
42 | AvailabilityAttr::getEnvironmentType(IIEnvironment->getName()); |
43 | return Environment == ET; |
44 | } |
45 | |
46 | static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, |
47 | const Decl *D) { |
48 | AvailabilityAttr const *PartialMatch = nullptr; |
49 | // Check each AvailabilityAttr to find the one for this platform. |
50 | // For multiple attributes with the same platform try to find one for this |
51 | // environment. |
52 | // The attribute is always on the FunctionDecl, not on the |
53 | // FunctionTemplateDecl. |
54 | if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(Val: D)) |
55 | D = FTD->getTemplatedDecl(); |
56 | for (const auto *A : D->attrs()) { |
57 | if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) { |
58 | // FIXME: this is copied from CheckAvailability. We should try to |
59 | // de-duplicate. |
60 | |
61 | // Check if this is an App Extension "platform", and if so chop off |
62 | // the suffix for matching with the actual platform. |
63 | StringRef ActualPlatform = Avail->getPlatform()->getName(); |
64 | StringRef RealizedPlatform = ActualPlatform; |
65 | if (Context.getLangOpts().AppExt) { |
66 | size_t suffix = RealizedPlatform.rfind("_app_extension"); |
67 | if (suffix != StringRef::npos) |
68 | RealizedPlatform = RealizedPlatform.slice(0, suffix); |
69 | } |
70 | |
71 | StringRef TargetPlatform = Context.getTargetInfo().getPlatformName(); |
72 | |
73 | // Match the platform name. |
74 | if (RealizedPlatform == TargetPlatform) { |
75 | // Find the best matching attribute for this environment |
76 | if (hasMatchingEnvironmentOrNone(Context, Avail)) |
77 | return Avail; |
78 | PartialMatch = Avail; |
79 | } |
80 | } |
81 | } |
82 | return PartialMatch; |
83 | } |
84 | |
85 | /// The diagnostic we should emit for \c D, and the declaration that |
86 | /// originated it, or \c AR_Available. |
87 | /// |
88 | /// \param D The declaration to check. |
89 | /// \param Message If non-null, this will be populated with the message from |
90 | /// the availability attribute that is selected. |
91 | /// \param ClassReceiver If we're checking the method of a class message |
92 | /// send, the class. Otherwise nullptr. |
93 | std::pair<AvailabilityResult, const NamedDecl *> |
94 | Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message, |
95 | ObjCInterfaceDecl *ClassReceiver) { |
96 | AvailabilityResult Result = D->getAvailability(Message); |
97 | |
98 | // For typedefs, if the typedef declaration appears available look |
99 | // to the underlying type to see if it is more restrictive. |
100 | while (const auto *TD = dyn_cast<TypedefNameDecl>(Val: D)) { |
101 | if (Result != AR_Available) |
102 | break; |
103 | for (const Type *T = TD->getUnderlyingType().getTypePtr(); /**/; /**/) { |
104 | if (auto *TT = dyn_cast<TagType>(Val: T)) { |
105 | D = TT->getDecl(); |
106 | } else if (isa<SubstTemplateTypeParmType>(Val: T)) { |
107 | // A Subst* node represents a use through a template. |
108 | // Any uses of the underlying declaration happened through it's template |
109 | // specialization. |
110 | goto done; |
111 | } else { |
112 | const Type *NextT = |
113 | T->getLocallyUnqualifiedSingleStepDesugaredType().getTypePtr(); |
114 | if (NextT == T) |
115 | goto done; |
116 | T = NextT; |
117 | continue; |
118 | } |
119 | Result = D->getAvailability(Message); |
120 | break; |
121 | } |
122 | } |
123 | done: |
124 | // For alias templates, get the underlying declaration. |
125 | if (const auto *ADecl = dyn_cast<TypeAliasTemplateDecl>(Val: D)) { |
126 | D = ADecl->getTemplatedDecl(); |
127 | Result = D->getAvailability(Message); |
128 | } |
129 | |
130 | // Forward class declarations get their attributes from their definition. |
131 | if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(Val: D)) { |
132 | if (IDecl->getDefinition()) { |
133 | D = IDecl->getDefinition(); |
134 | Result = D->getAvailability(Message); |
135 | } |
136 | } |
137 | |
138 | if (const auto *ECD = dyn_cast<EnumConstantDecl>(Val: D)) |
139 | if (Result == AR_Available) { |
140 | const DeclContext *DC = ECD->getDeclContext(); |
141 | if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) { |
142 | Result = TheEnumDecl->getAvailability(Message); |
143 | D = TheEnumDecl; |
144 | } |
145 | } |
146 | |
147 | // For +new, infer availability from -init. |
148 | if (const auto *MD = dyn_cast<ObjCMethodDecl>(Val: D)) { |
149 | if (ObjC().NSAPIObj && ClassReceiver) { |
150 | ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod( |
151 | Sel: ObjC().NSAPIObj->getInitSelector()); |
152 | if (Init && Result == AR_Available && MD->isClassMethod() && |
153 | MD->getSelector() == ObjC().NSAPIObj->getNewSelector() && |
154 | MD->definedInNSObject(getASTContext())) { |
155 | Result = Init->getAvailability(Message); |
156 | D = Init; |
157 | } |
158 | } |
159 | } |
160 | |
161 | return {Result, D}; |
162 | } |
163 | |
164 | /// whether we should emit a diagnostic for \c K and \c DeclVersion in |
165 | /// the context of \c Ctx. For example, we should emit an unavailable diagnostic |
166 | /// in a deprecated context, but not the other way around. |
167 | static bool ShouldDiagnoseAvailabilityInContext( |
168 | Sema &S, AvailabilityResult K, VersionTuple DeclVersion, |
169 | const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) { |
170 | assert(K != AR_Available && "Expected an unavailable declaration here!"); |
171 | |
172 | // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic. |
173 | auto DeclLoc = Ctx->getBeginLoc(); |
174 | // This is only a problem in Foundation's C++ implementation for CF_OPTIONS. |
175 | if (DeclLoc.isMacroID() && S.getLangOpts().CPlusPlus && |
176 | isa<TypedefDecl>(Val: OffendingDecl)) { |
177 | StringRef MacroName = S.getPreprocessor().getImmediateMacroName(Loc: DeclLoc); |
178 | if (MacroName == "CF_OPTIONS"|| MacroName == "OBJC_OPTIONS"|| |
179 | MacroName == "SWIFT_OPTIONS"|| MacroName == "NS_OPTIONS") { |
180 | return false; |
181 | } |
182 | } |
183 | |
184 | // In HLSL, skip emitting diagnostic if the diagnostic mode is not set to |
185 | // strict (-fhlsl-strict-availability), or if the target is library and the |
186 | // availability is restricted to a specific environment/shader stage. |
187 | // For libraries the availability will be checked later in |
188 | // DiagnoseHLSLAvailability class once where the specific environment/shader |
189 | // stage of the caller is known. |
190 | if (S.getLangOpts().HLSL) { |
191 | if (!S.getLangOpts().HLSLStrictAvailability || |
192 | (DeclEnv != nullptr && |
193 | S.getASTContext().getTargetInfo().getTriple().getEnvironment() == |
194 | llvm::Triple::EnvironmentType::Library)) |
195 | return false; |
196 | } |
197 | |
198 | if (K == AR_Deprecated) { |
199 | if (const auto *VD = dyn_cast<VarDecl>(Val: OffendingDecl)) |
200 | if (VD->isLocalVarDeclOrParm() && VD->isDeprecated()) |
201 | return true; |
202 | } |
203 | |
204 | // Checks if we should emit the availability diagnostic in the context of C. |
205 | auto CheckContext = [&](const Decl *C) { |
206 | if (K == AR_NotYetIntroduced) { |
207 | if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C)) |
208 | if (AA->getIntroduced() >= DeclVersion && |
209 | AA->getEnvironment() == DeclEnv) |
210 | return true; |
211 | } else if (K == AR_Deprecated) { |
212 | if (C->isDeprecated()) |
213 | return true; |
214 | } else if (K == AR_Unavailable) { |
215 | // It is perfectly fine to refer to an 'unavailable' Objective-C method |
216 | // when it is referenced from within the @implementation itself. In this |
217 | // context, we interpret unavailable as a form of access control. |
218 | if (const auto *MD = dyn_cast<ObjCMethodDecl>(Val: OffendingDecl)) { |
219 | if (const auto *Impl = dyn_cast<ObjCImplDecl>(Val: C)) { |
220 | if (MD->getClassInterface() == Impl->getClassInterface()) |
221 | return true; |
222 | } |
223 | } |
224 | } |
225 | |
226 | if (C->isUnavailable()) |
227 | return true; |
228 | return false; |
229 | }; |
230 | |
231 | do { |
232 | if (CheckContext(Ctx)) |
233 | return false; |
234 | |
235 | // An implementation implicitly has the availability of the interface. |
236 | // Unless it is "+load" method. |
237 | if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Val: Ctx)) |
238 | if (MethodD->isClassMethod() && |
239 | MethodD->getSelector().getAsString() == "load") |
240 | return true; |
241 | |
242 | if (const auto *CatOrImpl = dyn_cast<ObjCImplDecl>(Val: Ctx)) { |
243 | if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface()) |
244 | if (CheckContext(Interface)) |
245 | return false; |
246 | } |
247 | // A category implicitly has the availability of the interface. |
248 | else if (const auto *CatD = dyn_cast<ObjCCategoryDecl>(Val: Ctx)) |
249 | if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface()) |
250 | if (CheckContext(Interface)) |
251 | return false; |
252 | } while ((Ctx = cast_or_null<Decl>(Val: Ctx->getDeclContext()))); |
253 | |
254 | return true; |
255 | } |
256 | |
257 | static unsigned getAvailabilityDiagnosticKind( |
258 | const ASTContext &Context, const VersionTuple &DeploymentVersion, |
259 | const VersionTuple &DeclVersion, bool HasMatchingEnv) { |
260 | const auto &Triple = Context.getTargetInfo().getTriple(); |
261 | VersionTuple ForceAvailabilityFromVersion; |
262 | switch (Triple.getOS()) { |
263 | // For iOS, emit the diagnostic even if -Wunguarded-availability is |
264 | // not specified for deployment targets >= to iOS 11 or equivalent or |
265 | // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or |
266 | // later. |
267 | case llvm::Triple::IOS: |
268 | case llvm::Triple::TvOS: |
269 | ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11); |
270 | break; |
271 | case llvm::Triple::WatchOS: |
272 | ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4); |
273 | break; |
274 | case llvm::Triple::Darwin: |
275 | case llvm::Triple::MacOSX: |
276 | ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13); |
277 | break; |
278 | // For HLSL, use diagnostic from HLSLAvailability group which |
279 | // are reported as errors by default and in strict diagnostic mode |
280 | // (-fhlsl-strict-availability) and as warnings in relaxed diagnostic |
281 | // mode (-Wno-error=hlsl-availability) |
282 | case llvm::Triple::ShaderModel: |
283 | return HasMatchingEnv ? diag::warn_hlsl_availability |
284 | : diag::warn_hlsl_availability_unavailable; |
285 | default: |
286 | // New Apple targets should always warn about availability. |
287 | ForceAvailabilityFromVersion = |
288 | (Triple.getVendor() == llvm::Triple::Apple) |
289 | ? VersionTuple(/*Major=*/0, 0) |
290 | : VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1); |
291 | } |
292 | if (DeploymentVersion >= ForceAvailabilityFromVersion || |
293 | DeclVersion >= ForceAvailabilityFromVersion) |
294 | return HasMatchingEnv ? diag::warn_unguarded_availability_new |
295 | : diag::warn_unguarded_availability_unavailable_new; |
296 | return HasMatchingEnv ? diag::warn_unguarded_availability |
297 | : diag::warn_unguarded_availability_unavailable; |
298 | } |
299 | |
300 | static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) { |
301 | for (Decl *Ctx = OrigCtx; Ctx; |
302 | Ctx = cast_or_null<Decl>(Val: Ctx->getDeclContext())) { |
303 | if (isa<TagDecl>(Val: Ctx) || isa<FunctionDecl>(Val: Ctx) || isa<ObjCMethodDecl>(Val: Ctx)) |
304 | return cast<NamedDecl>(Val: Ctx); |
305 | if (auto *CD = dyn_cast<ObjCContainerDecl>(Val: Ctx)) { |
306 | if (auto *Imp = dyn_cast<ObjCImplDecl>(Val: Ctx)) |
307 | return Imp->getClassInterface(); |
308 | return CD; |
309 | } |
310 | } |
311 | |
312 | return dyn_cast<NamedDecl>(Val: OrigCtx); |
313 | } |
314 | |
315 | namespace { |
316 | |
317 | struct AttributeInsertion { |
318 | StringRef Prefix; |
319 | SourceLocation Loc; |
320 | StringRef Suffix; |
321 | |
322 | static AttributeInsertion createInsertionAfter(const NamedDecl *D) { |
323 | return {" ", D->getEndLoc(), ""}; |
324 | } |
325 | static AttributeInsertion createInsertionAfter(SourceLocation Loc) { |
326 | return {.Prefix: " ", .Loc: Loc, .Suffix: ""}; |
327 | } |
328 | static AttributeInsertion createInsertionBefore(const NamedDecl *D) { |
329 | return {"", D->getBeginLoc(), "\n"}; |
330 | } |
331 | }; |
332 | |
333 | } // end anonymous namespace |
334 | |
335 | /// Tries to parse a string as ObjC method name. |
336 | /// |
337 | /// \param Name The string to parse. Expected to originate from availability |
338 | /// attribute argument. |
339 | /// \param SlotNames The vector that will be populated with slot names. In case |
340 | /// of unsuccessful parsing can contain invalid data. |
341 | /// \returns A number of method parameters if parsing was successful, |
342 | /// std::nullopt otherwise. |
343 | static std::optional<unsigned> |
344 | tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &SlotNames, |
345 | const LangOptions &LangOpts) { |
346 | // Accept replacements starting with - or + as valid ObjC method names. |
347 | if (!Name.empty() && (Name.front() == '-' || Name.front() == '+')) |
348 | Name = Name.drop_front(N: 1); |
349 | if (Name.empty()) |
350 | return std::nullopt; |
351 | Name.split(A&: SlotNames, Separator: ':'); |
352 | unsigned NumParams; |
353 | if (Name.back() == ':') { |
354 | // Remove an empty string at the end that doesn't represent any slot. |
355 | SlotNames.pop_back(); |
356 | NumParams = SlotNames.size(); |
357 | } else { |
358 | if (SlotNames.size() != 1) |
359 | // Not a valid method name, just a colon-separated string. |
360 | return std::nullopt; |
361 | NumParams = 0; |
362 | } |
363 | // Verify all slot names are valid. |
364 | bool AllowDollar = LangOpts.DollarIdents; |
365 | for (StringRef S : SlotNames) { |
366 | if (S.empty()) |
367 | continue; |
368 | if (!isValidAsciiIdentifier(S, AllowDollar)) |
369 | return std::nullopt; |
370 | } |
371 | return NumParams; |
372 | } |
373 | |
374 | /// Returns a source location in which it's appropriate to insert a new |
375 | /// attribute for the given declaration \D. |
376 | static std::optional<AttributeInsertion> |
377 | createAttributeInsertion(const NamedDecl *D, const SourceManager &SM, |
378 | const LangOptions &LangOpts) { |
379 | if (isa<ObjCPropertyDecl>(Val: D)) |
380 | return AttributeInsertion::createInsertionAfter(D); |
381 | if (const auto *MD = dyn_cast<ObjCMethodDecl>(Val: D)) { |
382 | if (MD->hasBody()) |
383 | return std::nullopt; |
384 | return AttributeInsertion::createInsertionAfter(D); |
385 | } |
386 | if (const auto *TD = dyn_cast<TagDecl>(Val: D)) { |
387 | SourceLocation Loc = |
388 | Lexer::getLocForEndOfToken(Loc: TD->getInnerLocStart(), Offset: 0, SM, LangOpts); |
389 | if (Loc.isInvalid()) |
390 | return std::nullopt; |
391 | // Insert after the 'struct'/whatever keyword. |
392 | return AttributeInsertion::createInsertionAfter(Loc); |
393 | } |
394 | return AttributeInsertion::createInsertionBefore(D); |
395 | } |
396 | |
397 | /// Actually emit an availability diagnostic for a reference to an unavailable |
398 | /// decl. |
399 | /// |
400 | /// \param Ctx The context that the reference occurred in |
401 | /// \param ReferringDecl The exact declaration that was referenced. |
402 | /// \param OffendingDecl A related decl to \c ReferringDecl that has an |
403 | /// availability attribute corresponding to \c K attached to it. Note that this |
404 | /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and |
405 | /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl |
406 | /// and OffendingDecl is the EnumDecl. |
407 | static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, |
408 | Decl *Ctx, const NamedDecl *ReferringDecl, |
409 | const NamedDecl *OffendingDecl, |
410 | StringRef Message, |
411 | ArrayRef<SourceLocation> Locs, |
412 | const ObjCInterfaceDecl *UnknownObjCClass, |
413 | const ObjCPropertyDecl *ObjCProperty, |
414 | bool ObjCPropertyAccess) { |
415 | // Diagnostics for deprecated or unavailable. |
416 | unsigned diag, diag_message, diag_fwdclass_message; |
417 | unsigned diag_available_here = diag::note_availability_specified_here; |
418 | SourceLocation NoteLocation = OffendingDecl->getLocation(); |
419 | |
420 | // Matches 'diag::note_property_attribute' options. |
421 | unsigned property_note_select; |
422 | |
423 | // Matches diag::note_availability_specified_here. |
424 | unsigned available_here_select_kind; |
425 | |
426 | VersionTuple DeclVersion; |
427 | const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl); |
428 | const IdentifierInfo *IIEnv = nullptr; |
429 | if (AA) { |
430 | DeclVersion = AA->getIntroduced(); |
431 | IIEnv = AA->getEnvironment(); |
432 | } |
433 | |
434 | if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, DeclEnv: IIEnv, Ctx, |
435 | OffendingDecl)) |
436 | return; |
437 | |
438 | SourceLocation Loc = Locs.front(); |
439 | |
440 | // The declaration can have multiple availability attributes, we are looking |
441 | // at one of them. |
442 | if (AA && AA->isInherited()) { |
443 | for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl; |
444 | Redecl = Redecl->getPreviousDecl()) { |
445 | const AvailabilityAttr *AForRedecl = |
446 | getAttrForPlatform(S.Context, Redecl); |
447 | if (AForRedecl && !AForRedecl->isInherited()) { |
448 | // If D is a declaration with inherited attributes, the note should |
449 | // point to the declaration with actual attributes. |
450 | NoteLocation = Redecl->getLocation(); |
451 | break; |
452 | } |
453 | } |
454 | } |
455 | |
456 | switch (K) { |
457 | case AR_NotYetIntroduced: { |
458 | // We would like to emit the diagnostic even if -Wunguarded-availability is |
459 | // not specified for deployment targets >= to iOS 11 or equivalent or |
460 | // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or |
461 | // later. |
462 | assert(AA != nullptr && "expecting valid availability attribute"); |
463 | VersionTuple Introduced = AA->getIntroduced(); |
464 | bool EnvironmentMatchesOrNone = |
465 | hasMatchingEnvironmentOrNone(S.getASTContext(), AA); |
466 | |
467 | const TargetInfo &TI = S.getASTContext().getTargetInfo(); |
468 | std::string PlatformName( |
469 | AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); |
470 | llvm::StringRef TargetEnvironment( |
471 | llvm::Triple::getEnvironmentTypeName(Kind: TI.getTriple().getEnvironment())); |
472 | llvm::StringRef AttrEnvironment = |
473 | AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; |
474 | bool UseEnvironment = |
475 | (!AttrEnvironment.empty() && !TargetEnvironment.empty()); |
476 | |
477 | unsigned DiagKind = getAvailabilityDiagnosticKind( |
478 | Context: S.Context, DeploymentVersion: S.Context.getTargetInfo().getPlatformMinVersion(), |
479 | DeclVersion: Introduced, HasMatchingEnv: EnvironmentMatchesOrNone); |
480 | |
481 | S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName |
482 | << Introduced.getAsString() << UseEnvironment |
483 | << TargetEnvironment; |
484 | |
485 | S.Diag(OffendingDecl->getLocation(), |
486 | diag::note_partial_availability_specified_here) |
487 | << OffendingDecl << PlatformName << Introduced.getAsString() |
488 | << S.Context.getTargetInfo().getPlatformMinVersion().getAsString() |
489 | << UseEnvironment << AttrEnvironment << TargetEnvironment; |
490 | |
491 | // Do not offer to silence the warning or fixits for HLSL |
492 | if (S.getLangOpts().HLSL) |
493 | return; |
494 | |
495 | if (const auto *Enclosing = findEnclosingDeclToAnnotate(OrigCtx: Ctx)) { |
496 | if (const auto *TD = dyn_cast<TagDecl>(Val: Enclosing)) |
497 | if (TD->getDeclName().isEmpty()) { |
498 | S.Diag(TD->getLocation(), |
499 | diag::note_decl_unguarded_availability_silence) |
500 | << /*Anonymous*/ 1 << TD->getKindName(); |
501 | return; |
502 | } |
503 | auto FixitNoteDiag = |
504 | S.Diag(Enclosing->getLocation(), |
505 | diag::note_decl_unguarded_availability_silence) |
506 | << /*Named*/ 0 << Enclosing; |
507 | // Don't offer a fixit for declarations with availability attributes. |
508 | if (Enclosing->hasAttr<AvailabilityAttr>()) |
509 | return; |
510 | Preprocessor &PP = S.getPreprocessor(); |
511 | if (!PP.isMacroDefined(Id: "API_AVAILABLE")) |
512 | return; |
513 | std::optional<AttributeInsertion> Insertion = createAttributeInsertion( |
514 | D: Enclosing, SM: S.getSourceManager(), LangOpts: S.getLangOpts()); |
515 | if (!Insertion) |
516 | return; |
517 | StringRef PlatformName = |
518 | S.getASTContext().getTargetInfo().getPlatformName(); |
519 | |
520 | // Apple's API_AVAILABLE macro expands roughly like this. |
521 | // API_AVAILABLE(ios(17.0)) |
522 | // __attribute__((availability(__API_AVAILABLE_PLATFORM_ios(17.0))) |
523 | // __attribute__((availability(ios,introduced=17.0))) |
524 | // In order to figure out which platform name to use in the API_AVAILABLE |
525 | // macro, the associated __API_AVAILABLE_PLATFORM_ macro needs to be |
526 | // found. The __API_AVAILABLE_PLATFORM_ macros aren't consistent about |
527 | // using the canonical platform name, source spelling name, or one of the |
528 | // other supported names (i.e. one of the keys in canonicalizePlatformName |
529 | // that's neither). Check all of the supported names for a match. |
530 | std::vector<StringRef> EquivalentPlatforms = |
531 | AvailabilityAttr::equivalentPlatformNames(PlatformName); |
532 | llvm::Twine MacroPrefix = "__API_AVAILABLE_PLATFORM_"; |
533 | auto AvailablePlatform = |
534 | llvm::find_if(Range&: EquivalentPlatforms, P: [&](StringRef EquivalentPlatform) { |
535 | return PP.isMacroDefined(Id: (MacroPrefix + EquivalentPlatform).str()); |
536 | }); |
537 | if (AvailablePlatform == EquivalentPlatforms.end()) |
538 | return; |
539 | std::string Introduced = |
540 | OffendingDecl->getVersionIntroduced().getAsString(); |
541 | FixitNoteDiag << FixItHint::CreateInsertion( |
542 | InsertionLoc: Insertion->Loc, |
543 | Code: (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE("+ |
544 | *AvailablePlatform + "("+ Introduced + "))"+ Insertion->Suffix) |
545 | .str()); |
546 | } |
547 | return; |
548 | } |
549 | case AR_Deprecated: |
550 | if (ObjCPropertyAccess) |
551 | diag = diag::warn_property_method_deprecated; |
552 | else if (S.currentEvaluationContext().IsCaseExpr) |
553 | diag = diag::warn_deprecated_switch_case; |
554 | else |
555 | diag = diag::warn_deprecated; |
556 | |
557 | diag_message = diag::warn_deprecated_message; |
558 | diag_fwdclass_message = diag::warn_deprecated_fwdclass_message; |
559 | property_note_select = /* deprecated */ 0; |
560 | available_here_select_kind = /* deprecated */ 2; |
561 | if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>()) |
562 | NoteLocation = AL->getLocation(); |
563 | break; |
564 | |
565 | case AR_Unavailable: |
566 | diag = !ObjCPropertyAccess ? diag::err_unavailable |
567 | : diag::err_property_method_unavailable; |
568 | diag_message = diag::err_unavailable_message; |
569 | diag_fwdclass_message = diag::warn_unavailable_fwdclass_message; |
570 | property_note_select = /* unavailable */ 1; |
571 | available_here_select_kind = /* unavailable */ 0; |
572 | |
573 | if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) { |
574 | if (AL->isImplicit() && AL->getImplicitReason()) { |
575 | // Most of these failures are due to extra restrictions in ARC; |
576 | // reflect that in the primary diagnostic when applicable. |
577 | auto flagARCError = [&] { |
578 | if (S.getLangOpts().ObjCAutoRefCount && |
579 | S.getSourceManager().isInSystemHeader( |
580 | OffendingDecl->getLocation())) |
581 | diag = diag::err_unavailable_in_arc; |
582 | }; |
583 | |
584 | switch (AL->getImplicitReason()) { |
585 | case UnavailableAttr::IR_None: break; |
586 | |
587 | case UnavailableAttr::IR_ARCForbiddenType: |
588 | flagARCError(); |
589 | diag_available_here = diag::note_arc_forbidden_type; |
590 | break; |
591 | |
592 | case UnavailableAttr::IR_ForbiddenWeak: |
593 | if (S.getLangOpts().ObjCWeakRuntime) |
594 | diag_available_here = diag::note_arc_weak_disabled; |
595 | else |
596 | diag_available_here = diag::note_arc_weak_no_runtime; |
597 | break; |
598 | |
599 | case UnavailableAttr::IR_ARCForbiddenConversion: |
600 | flagARCError(); |
601 | diag_available_here = diag::note_performs_forbidden_arc_conversion; |
602 | break; |
603 | |
604 | case UnavailableAttr::IR_ARCInitReturnsUnrelated: |
605 | flagARCError(); |
606 | diag_available_here = diag::note_arc_init_returns_unrelated; |
607 | break; |
608 | |
609 | case UnavailableAttr::IR_ARCFieldWithOwnership: |
610 | flagARCError(); |
611 | diag_available_here = diag::note_arc_field_with_ownership; |
612 | break; |
613 | } |
614 | } |
615 | } |
616 | break; |
617 | |
618 | case AR_Available: |
619 | llvm_unreachable("Warning for availability of available declaration?"); |
620 | } |
621 | |
622 | SmallVector<FixItHint, 12> FixIts; |
623 | if (K == AR_Deprecated) { |
624 | StringRef Replacement; |
625 | if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>()) |
626 | Replacement = AL->getReplacement(); |
627 | if (auto AL = getAttrForPlatform(S.Context, OffendingDecl)) |
628 | Replacement = AL->getReplacement(); |
629 | |
630 | CharSourceRange UseRange; |
631 | if (!Replacement.empty()) |
632 | UseRange = |
633 | CharSourceRange::getCharRange(B: Loc, E: S.getLocForEndOfToken(Loc)); |
634 | if (UseRange.isValid()) { |
635 | if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(Val: ReferringDecl)) { |
636 | Selector Sel = MethodDecl->getSelector(); |
637 | SmallVector<StringRef, 12> SelectorSlotNames; |
638 | std::optional<unsigned> NumParams = tryParseObjCMethodName( |
639 | Name: Replacement, SlotNames&: SelectorSlotNames, LangOpts: S.getLangOpts()); |
640 | if (NumParams && *NumParams == Sel.getNumArgs()) { |
641 | assert(SelectorSlotNames.size() == Locs.size()); |
642 | for (unsigned I = 0; I < Locs.size(); ++I) { |
643 | if (!Sel.getNameForSlot(argIndex: I).empty()) { |
644 | CharSourceRange NameRange = CharSourceRange::getCharRange( |
645 | B: Locs[I], E: S.getLocForEndOfToken(Loc: Locs[I])); |
646 | FixIts.push_back(Elt: FixItHint::CreateReplacement( |
647 | RemoveRange: NameRange, Code: SelectorSlotNames[I])); |
648 | } else |
649 | FixIts.push_back( |
650 | Elt: FixItHint::CreateInsertion(InsertionLoc: Locs[I], Code: SelectorSlotNames[I])); |
651 | } |
652 | } else |
653 | FixIts.push_back(Elt: FixItHint::CreateReplacement(RemoveRange: UseRange, Code: Replacement)); |
654 | } else |
655 | FixIts.push_back(Elt: FixItHint::CreateReplacement(RemoveRange: UseRange, Code: Replacement)); |
656 | } |
657 | } |
658 | |
659 | // We emit deprecation warning for deprecated specializations |
660 | // when their instantiation stacks originate outside |
661 | // of a system header, even if the diagnostics is suppresed at the |
662 | // point of definition. |
663 | SourceLocation InstantiationLoc = |
664 | S.getTopMostPointOfInstantiation(ReferringDecl); |
665 | bool ShouldAllowWarningInSystemHeader = |
666 | InstantiationLoc != Loc && |
667 | !S.getSourceManager().isInSystemHeader(Loc: InstantiationLoc); |
668 | struct AllowWarningInSystemHeaders { |
669 | AllowWarningInSystemHeaders(DiagnosticsEngine &E, |
670 | bool AllowWarningInSystemHeaders) |
671 | : Engine(E), Prev(E.getSuppressSystemWarnings()) { |
672 | E.setSuppressSystemWarnings(!AllowWarningInSystemHeaders); |
673 | } |
674 | ~AllowWarningInSystemHeaders() { Engine.setSuppressSystemWarnings(Prev); } |
675 | |
676 | private: |
677 | DiagnosticsEngine &Engine; |
678 | bool Prev; |
679 | } SystemWarningOverrideRAII(S.getDiagnostics(), |
680 | ShouldAllowWarningInSystemHeader); |
681 | |
682 | if (!Message.empty()) { |
683 | S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts; |
684 | if (ObjCProperty) |
685 | S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) |
686 | << ObjCProperty->getDeclName() << property_note_select; |
687 | } else if (!UnknownObjCClass) { |
688 | S.Diag(Loc, diag) << ReferringDecl << FixIts; |
689 | if (ObjCProperty) |
690 | S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) |
691 | << ObjCProperty->getDeclName() << property_note_select; |
692 | } else { |
693 | S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts; |
694 | S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); |
695 | } |
696 | |
697 | S.Diag(NoteLocation, diag_available_here) |
698 | << OffendingDecl << available_here_select_kind; |
699 | } |
700 | |
701 | void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) { |
702 | assert(DD.Kind == DelayedDiagnostic::Availability && |
703 | "Expected an availability diagnostic here"); |
704 | |
705 | DD.Triggered = true; |
706 | DoEmitAvailabilityWarning( |
707 | S&: *this, K: DD.getAvailabilityResult(), Ctx, ReferringDecl: DD.getAvailabilityReferringDecl(), |
708 | OffendingDecl: DD.getAvailabilityOffendingDecl(), Message: DD.getAvailabilityMessage(), |
709 | Locs: DD.getAvailabilitySelectorLocs(), UnknownObjCClass: DD.getUnknownObjCClass(), |
710 | ObjCProperty: DD.getObjCProperty(), ObjCPropertyAccess: false); |
711 | } |
712 | |
713 | static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR, |
714 | const NamedDecl *ReferringDecl, |
715 | const NamedDecl *OffendingDecl, |
716 | StringRef Message, |
717 | ArrayRef<SourceLocation> Locs, |
718 | const ObjCInterfaceDecl *UnknownObjCClass, |
719 | const ObjCPropertyDecl *ObjCProperty, |
720 | bool ObjCPropertyAccess) { |
721 | // Delay if we're currently parsing a declaration. |
722 | if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { |
723 | S.DelayedDiagnostics.add( |
724 | diag: DelayedDiagnostic::makeAvailability( |
725 | AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass, |
726 | ObjCProperty, Msg: Message, ObjCPropertyAccess)); |
727 | return; |
728 | } |
729 | |
730 | Decl *Ctx = cast<Decl>(Val: S.getCurLexicalContext()); |
731 | DoEmitAvailabilityWarning(S, K: AR, Ctx, ReferringDecl, OffendingDecl, |
732 | Message, Locs, UnknownObjCClass, ObjCProperty, |
733 | ObjCPropertyAccess); |
734 | } |
735 | |
736 | namespace { |
737 | |
738 | /// Returns true if the given statement can be a body-like child of \p Parent. |
739 | bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) { |
740 | switch (Parent->getStmtClass()) { |
741 | case Stmt::IfStmtClass: |
742 | return cast<IfStmt>(Val: Parent)->getThen() == S || |
743 | cast<IfStmt>(Val: Parent)->getElse() == S; |
744 | case Stmt::WhileStmtClass: |
745 | return cast<WhileStmt>(Val: Parent)->getBody() == S; |
746 | case Stmt::DoStmtClass: |
747 | return cast<DoStmt>(Val: Parent)->getBody() == S; |
748 | case Stmt::ForStmtClass: |
749 | return cast<ForStmt>(Val: Parent)->getBody() == S; |
750 | case Stmt::CXXForRangeStmtClass: |
751 | return cast<CXXForRangeStmt>(Val: Parent)->getBody() == S; |
752 | case Stmt::ObjCForCollectionStmtClass: |
753 | return cast<ObjCForCollectionStmt>(Val: Parent)->getBody() == S; |
754 | case Stmt::CaseStmtClass: |
755 | case Stmt::DefaultStmtClass: |
756 | return cast<SwitchCase>(Val: Parent)->getSubStmt() == S; |
757 | default: |
758 | return false; |
759 | } |
760 | } |
761 | |
762 | class StmtUSEFinder : public DynamicRecursiveASTVisitor { |
763 | const Stmt *Target; |
764 | |
765 | public: |
766 | bool VisitStmt(Stmt *S) override { return S != Target; } |
767 | |
768 | /// Returns true if the given statement is present in the given declaration. |
769 | static bool isContained(const Stmt *Target, const Decl *D) { |
770 | StmtUSEFinder Visitor; |
771 | Visitor.Target = Target; |
772 | return !Visitor.TraverseDecl(const_cast<Decl *>(D)); |
773 | } |
774 | }; |
775 | |
776 | /// Traverses the AST and finds the last statement that used a given |
777 | /// declaration. |
778 | class LastDeclUSEFinder : public DynamicRecursiveASTVisitor { |
779 | const Decl *D; |
780 | |
781 | public: |
782 | bool VisitDeclRefExpr(DeclRefExpr *DRE) override { |
783 | if (DRE->getDecl() == D) |
784 | return false; |
785 | return true; |
786 | } |
787 | |
788 | static const Stmt *findLastStmtThatUsesDecl(const Decl *D, |
789 | const CompoundStmt *Scope) { |
790 | LastDeclUSEFinder Visitor; |
791 | Visitor.D = D; |
792 | for (const Stmt *S : llvm::reverse(C: Scope->body())) { |
793 | if (!Visitor.TraverseStmt(const_cast<Stmt *>(S))) |
794 | return S; |
795 | } |
796 | return nullptr; |
797 | } |
798 | }; |
799 | |
800 | /// This class implements -Wunguarded-availability. |
801 | /// |
802 | /// This is done with a traversal of the AST of a function that makes reference |
803 | /// to a partially available declaration. Whenever we encounter an \c if of the |
804 | /// form: \c if(@available(...)), we use the version from the condition to visit |
805 | /// the then statement. |
806 | class DiagnoseUnguardedAvailability : public DynamicRecursiveASTVisitor { |
807 | Sema &SemaRef; |
808 | Decl *Ctx; |
809 | |
810 | /// Stack of potentially nested 'if (@available(...))'s. |
811 | SmallVector<VersionTuple, 8> AvailabilityStack; |
812 | SmallVector<const Stmt *, 16> StmtStack; |
813 | |
814 | void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range, |
815 | ObjCInterfaceDecl *ClassReceiver = nullptr); |
816 | |
817 | public: |
818 | DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx) |
819 | : SemaRef(SemaRef), Ctx(Ctx) { |
820 | AvailabilityStack.push_back( |
821 | Elt: SemaRef.Context.getTargetInfo().getPlatformMinVersion()); |
822 | } |
823 | |
824 | bool TraverseStmt(Stmt *S) override { |
825 | if (!S) |
826 | return true; |
827 | StmtStack.push_back(Elt: S); |
828 | bool Result = DynamicRecursiveASTVisitor::TraverseStmt(S); |
829 | StmtStack.pop_back(); |
830 | return Result; |
831 | } |
832 | |
833 | void IssueDiagnostics(Stmt *S) { TraverseStmt(S); } |
834 | |
835 | bool TraverseIfStmt(IfStmt *If) override; |
836 | |
837 | // for 'case X:' statements, don't bother looking at the 'X'; it can't lead |
838 | // to any useful diagnostics. |
839 | bool TraverseCaseStmt(CaseStmt *CS) override { |
840 | return TraverseStmt(S: CS->getSubStmt()); |
841 | } |
842 | |
843 | bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) override { |
844 | if (ObjCMethodDecl *D = Msg->getMethodDecl()) { |
845 | ObjCInterfaceDecl *ID = nullptr; |
846 | QualType ReceiverTy = Msg->getClassReceiver(); |
847 | if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType()) |
848 | ID = ReceiverTy->getAsObjCInterfaceType()->getInterface(); |
849 | |
850 | DiagnoseDeclAvailability( |
851 | D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID); |
852 | } |
853 | return true; |
854 | } |
855 | |
856 | bool VisitDeclRefExpr(DeclRefExpr *DRE) override { |
857 | DiagnoseDeclAvailability(DRE->getDecl(), |
858 | SourceRange(DRE->getBeginLoc(), DRE->getEndLoc())); |
859 | return true; |
860 | } |
861 | |
862 | bool VisitMemberExpr(MemberExpr *ME) override { |
863 | DiagnoseDeclAvailability(ME->getMemberDecl(), |
864 | SourceRange(ME->getBeginLoc(), ME->getEndLoc())); |
865 | return true; |
866 | } |
867 | |
868 | bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) override { |
869 | SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use) |
870 | << (!SemaRef.getLangOpts().ObjC); |
871 | return true; |
872 | } |
873 | |
874 | bool VisitTypeLoc(TypeLoc Ty) override; |
875 | }; |
876 | |
877 | void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( |
878 | NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) { |
879 | AvailabilityResult Result; |
880 | const NamedDecl *OffendingDecl; |
881 | std::tie(args&: Result, args&: OffendingDecl) = |
882 | SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, Message: nullptr, ClassReceiver: ReceiverClass); |
883 | if (Result != AR_Available) { |
884 | // All other diagnostic kinds have already been handled in |
885 | // DiagnoseAvailabilityOfDecl. |
886 | if (Result != AR_NotYetIntroduced) |
887 | return; |
888 | |
889 | const AvailabilityAttr *AA = |
890 | getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl); |
891 | assert(AA != nullptr && "expecting valid availability attribute"); |
892 | bool EnvironmentMatchesOrNone = |
893 | hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA); |
894 | VersionTuple Introduced = AA->getIntroduced(); |
895 | |
896 | if (EnvironmentMatchesOrNone && AvailabilityStack.back() >= Introduced) |
897 | return; |
898 | |
899 | // If the context of this function is less available than D, we should not |
900 | // emit a diagnostic. |
901 | if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, |
902 | AA->getEnvironment(), Ctx, |
903 | OffendingDecl)) |
904 | return; |
905 | |
906 | const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo(); |
907 | std::string PlatformName( |
908 | AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); |
909 | llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName()); |
910 | llvm::StringRef AttrEnvironment = |
911 | AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; |
912 | bool UseEnvironment = |
913 | (!AttrEnvironment.empty() && !TargetEnvironment.empty()); |
914 | |
915 | unsigned DiagKind = getAvailabilityDiagnosticKind( |
916 | Context: SemaRef.Context, |
917 | DeploymentVersion: SemaRef.Context.getTargetInfo().getPlatformMinVersion(), DeclVersion: Introduced, |
918 | HasMatchingEnv: EnvironmentMatchesOrNone); |
919 | |
920 | SemaRef.Diag(Range.getBegin(), DiagKind) |
921 | << Range << D << PlatformName << Introduced.getAsString() |
922 | << UseEnvironment << TargetEnvironment; |
923 | |
924 | SemaRef.Diag(OffendingDecl->getLocation(), |
925 | diag::note_partial_availability_specified_here) |
926 | << OffendingDecl << PlatformName << Introduced.getAsString() |
927 | << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString() |
928 | << UseEnvironment << AttrEnvironment << TargetEnvironment; |
929 | |
930 | // Do not offer to silence the warning or fixits for HLSL |
931 | if (SemaRef.getLangOpts().HLSL) |
932 | return; |
933 | |
934 | auto FixitDiag = |
935 | SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) |
936 | << Range << D |
937 | << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0 |
938 | : /*__builtin_available*/ 1); |
939 | |
940 | // Find the statement which should be enclosed in the if @available check. |
941 | if (StmtStack.empty()) |
942 | return; |
943 | const Stmt *StmtOfUse = StmtStack.back(); |
944 | const CompoundStmt *Scope = nullptr; |
945 | for (const Stmt *S : llvm::reverse(C&: StmtStack)) { |
946 | if (const auto *CS = dyn_cast<CompoundStmt>(Val: S)) { |
947 | Scope = CS; |
948 | break; |
949 | } |
950 | if (isBodyLikeChildStmt(S: StmtOfUse, Parent: S)) { |
951 | // The declaration won't be seen outside of the statement, so we don't |
952 | // have to wrap the uses of any declared variables in if (@available). |
953 | // Therefore we can avoid setting Scope here. |
954 | break; |
955 | } |
956 | StmtOfUse = S; |
957 | } |
958 | const Stmt *LastStmtOfUse = nullptr; |
959 | if (isa<DeclStmt>(Val: StmtOfUse) && Scope) { |
960 | for (const Decl *D : cast<DeclStmt>(Val: StmtOfUse)->decls()) { |
961 | if (StmtUSEFinder::isContained(Target: StmtStack.back(), D)) { |
962 | LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope); |
963 | break; |
964 | } |
965 | } |
966 | } |
967 | |
968 | const SourceManager &SM = SemaRef.getSourceManager(); |
969 | SourceLocation IfInsertionLoc = |
970 | SM.getExpansionLoc(Loc: StmtOfUse->getBeginLoc()); |
971 | SourceLocation StmtEndLoc = |
972 | SM.getExpansionRange( |
973 | Loc: (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc()) |
974 | .getEnd(); |
975 | if (SM.getFileID(SpellingLoc: IfInsertionLoc) != SM.getFileID(SpellingLoc: StmtEndLoc)) |
976 | return; |
977 | |
978 | StringRef Indentation = Lexer::getIndentationForLine(Loc: IfInsertionLoc, SM); |
979 | const char *ExtraIndentation = " "; |
980 | std::string FixItString; |
981 | llvm::raw_string_ostream FixItOS(FixItString); |
982 | FixItOS << "if ("<< (SemaRef.getLangOpts().ObjC ? "@available" |
983 | : "__builtin_available") |
984 | << "(" |
985 | << AvailabilityAttr::getPlatformNameSourceSpelling( |
986 | SemaRef.getASTContext().getTargetInfo().getPlatformName()) |
987 | << " "<< Introduced.getAsString() << ", *)) {\n" |
988 | << Indentation << ExtraIndentation; |
989 | FixitDiag << FixItHint::CreateInsertion(InsertionLoc: IfInsertionLoc, Code: FixItOS.str()); |
990 | SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken( |
991 | loc: StmtEndLoc, TKind: tok::semi, SM, LangOpts: SemaRef.getLangOpts(), |
992 | /*SkipTrailingWhitespaceAndNewLine=*/false); |
993 | if (ElseInsertionLoc.isInvalid()) |
994 | ElseInsertionLoc = |
995 | Lexer::getLocForEndOfToken(Loc: StmtEndLoc, Offset: 0, SM, LangOpts: SemaRef.getLangOpts()); |
996 | FixItOS.str().clear(); |
997 | FixItOS << "\n" |
998 | << Indentation << "} else {\n" |
999 | << Indentation << ExtraIndentation |
1000 | << "// Fallback on earlier versions\n" |
1001 | << Indentation << "}"; |
1002 | FixitDiag << FixItHint::CreateInsertion(InsertionLoc: ElseInsertionLoc, Code: FixItOS.str()); |
1003 | } |
1004 | } |
1005 | |
1006 | bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { |
1007 | const Type *TyPtr = Ty.getTypePtr(); |
1008 | SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()}; |
1009 | |
1010 | if (Range.isInvalid()) |
1011 | return true; |
1012 | |
1013 | if (const auto *TT = dyn_cast<TagType>(Val: TyPtr)) { |
1014 | TagDecl *TD = TT->getDecl(); |
1015 | DiagnoseDeclAvailability(TD, Range); |
1016 | |
1017 | } else if (const auto *TD = dyn_cast<TypedefType>(Val: TyPtr)) { |
1018 | TypedefNameDecl *D = TD->getDecl(); |
1019 | DiagnoseDeclAvailability(D, Range); |
1020 | |
1021 | } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(Val: TyPtr)) { |
1022 | if (NamedDecl *D = ObjCO->getInterface()) |
1023 | DiagnoseDeclAvailability(D, Range); |
1024 | } |
1025 | |
1026 | return true; |
1027 | } |
1028 | |
1029 | struct ExtractedAvailabilityExpr { |
1030 | const ObjCAvailabilityCheckExpr *E = nullptr; |
1031 | bool isNegated = false; |
1032 | }; |
1033 | |
1034 | ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) { |
1035 | const auto *E = IfCond; |
1036 | bool IsNegated = false; |
1037 | while (true) { |
1038 | E = E->IgnoreParens(); |
1039 | if (const auto *AE = dyn_cast<ObjCAvailabilityCheckExpr>(Val: E)) { |
1040 | return ExtractedAvailabilityExpr{.E: AE, .isNegated: IsNegated}; |
1041 | } |
1042 | |
1043 | const auto *UO = dyn_cast<UnaryOperator>(Val: E); |
1044 | if (!UO || UO->getOpcode() != UO_LNot) { |
1045 | return ExtractedAvailabilityExpr{}; |
1046 | } |
1047 | E = UO->getSubExpr(); |
1048 | IsNegated = !IsNegated; |
1049 | } |
1050 | } |
1051 | |
1052 | bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { |
1053 | ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(IfCond: If->getCond()); |
1054 | if (!IfCond.E) { |
1055 | // This isn't an availability checking 'if', we can just continue. |
1056 | return DynamicRecursiveASTVisitor::TraverseIfStmt(If); |
1057 | } |
1058 | |
1059 | VersionTuple CondVersion = IfCond.E->getVersion(); |
1060 | // If we're using the '*' case here or if this check is redundant, then we |
1061 | // use the enclosing version to check both branches. |
1062 | if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) { |
1063 | return TraverseStmt(S: If->getThen()) && TraverseStmt(S: If->getElse()); |
1064 | } |
1065 | |
1066 | auto *Guarded = If->getThen(); |
1067 | auto *Unguarded = If->getElse(); |
1068 | if (IfCond.isNegated) { |
1069 | std::swap(a&: Guarded, b&: Unguarded); |
1070 | } |
1071 | |
1072 | AvailabilityStack.push_back(Elt: CondVersion); |
1073 | bool ShouldContinue = TraverseStmt(S: Guarded); |
1074 | AvailabilityStack.pop_back(); |
1075 | |
1076 | return ShouldContinue && TraverseStmt(S: Unguarded); |
1077 | } |
1078 | |
1079 | } // end anonymous namespace |
1080 | |
1081 | void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { |
1082 | Stmt *Body = nullptr; |
1083 | |
1084 | if (auto *FD = D->getAsFunction()) { |
1085 | Body = FD->getBody(); |
1086 | |
1087 | if (auto *CD = dyn_cast<CXXConstructorDecl>(Val: FD)) |
1088 | for (const CXXCtorInitializer *CI : CD->inits()) |
1089 | DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(CI->getInit()); |
1090 | |
1091 | } else if (auto *MD = dyn_cast<ObjCMethodDecl>(Val: D)) |
1092 | Body = MD->getBody(); |
1093 | else if (auto *BD = dyn_cast<BlockDecl>(Val: D)) |
1094 | Body = BD->getBody(); |
1095 | |
1096 | assert(Body && "Need a body here!"); |
1097 | |
1098 | DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(S: Body); |
1099 | } |
1100 | |
1101 | FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() { |
1102 | if (FunctionScopes.empty()) |
1103 | return nullptr; |
1104 | |
1105 | // Conservatively search the entire current function scope context for |
1106 | // availability violations. This ensures we always correctly analyze nested |
1107 | // classes, blocks, lambdas, etc. that may or may not be inside if(@available) |
1108 | // checks themselves. |
1109 | return FunctionScopes.front(); |
1110 | } |
1111 | |
1112 | void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, |
1113 | ArrayRef<SourceLocation> Locs, |
1114 | const ObjCInterfaceDecl *UnknownObjCClass, |
1115 | bool ObjCPropertyAccess, |
1116 | bool AvoidPartialAvailabilityChecks, |
1117 | ObjCInterfaceDecl *ClassReceiver) { |
1118 | |
1119 | std::string Message; |
1120 | AvailabilityResult Result; |
1121 | const NamedDecl* OffendingDecl; |
1122 | // See if this declaration is unavailable, deprecated, or partial. |
1123 | std::tie(args&: Result, args&: OffendingDecl) = |
1124 | ShouldDiagnoseAvailabilityOfDecl(D, Message: &Message, ClassReceiver); |
1125 | if (Result == AR_Available) |
1126 | return; |
1127 | |
1128 | if (Result == AR_NotYetIntroduced) { |
1129 | if (AvoidPartialAvailabilityChecks) |
1130 | return; |
1131 | |
1132 | // We need to know the @available context in the current function to |
1133 | // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that |
1134 | // when we're done parsing the current function. |
1135 | if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) { |
1136 | Context->HasPotentialAvailabilityViolations = true; |
1137 | return; |
1138 | } |
1139 | } |
1140 | |
1141 | const ObjCPropertyDecl *ObjCPDecl = nullptr; |
1142 | if (const auto *MD = dyn_cast<ObjCMethodDecl>(Val: D)) { |
1143 | if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { |
1144 | AvailabilityResult PDeclResult = PD->getAvailability(nullptr); |
1145 | if (PDeclResult == Result) |
1146 | ObjCPDecl = PD; |
1147 | } |
1148 | } |
1149 | |
1150 | EmitAvailabilityWarning(S&: *this, AR: Result, ReferringDecl: D, OffendingDecl, Message, Locs, |
1151 | UnknownObjCClass, ObjCProperty: ObjCPDecl, ObjCPropertyAccess); |
1152 | } |
1153 | |
1154 | void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, |
1155 | ArrayRef<SourceLocation> Locs) { |
1156 | DiagnoseAvailabilityOfDecl(D, Locs, /*UnknownObjCClass=*/nullptr, |
1157 | /*ObjCPropertyAccess=*/false, |
1158 | /*AvoidPartialAvailabilityChecks=*/false, |
1159 | /*ClassReceiver=*/nullptr); |
1160 | } |
1161 |
Definitions
- hasMatchingEnvironmentOrNone
- getAttrForPlatform
- ShouldDiagnoseAvailabilityOfDecl
- ShouldDiagnoseAvailabilityInContext
- getAvailabilityDiagnosticKind
- findEnclosingDeclToAnnotate
- AttributeInsertion
- createInsertionAfter
- createInsertionAfter
- createInsertionBefore
- tryParseObjCMethodName
- createAttributeInsertion
- DoEmitAvailabilityWarning
- handleDelayedAvailabilityCheck
- EmitAvailabilityWarning
- isBodyLikeChildStmt
- StmtUSEFinder
- VisitStmt
- isContained
- LastDeclUSEFinder
- VisitDeclRefExpr
- findLastStmtThatUsesDecl
- DiagnoseUnguardedAvailability
- DiagnoseUnguardedAvailability
- TraverseStmt
- IssueDiagnostics
- TraverseCaseStmt
- VisitObjCMessageExpr
- VisitDeclRefExpr
- VisitMemberExpr
- VisitObjCAvailabilityCheckExpr
- DiagnoseDeclAvailability
- VisitTypeLoc
- ExtractedAvailabilityExpr
- extractAvailabilityExpr
- TraverseIfStmt
- DiagnoseUnguardedAvailabilityViolations
- getCurFunctionAvailabilityContext
- DiagnoseAvailabilityOfDecl
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more