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
31using namespace clang;
32using namespace sema;
33
34static 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
46static 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.
93std::pair<AvailabilityResult, const NamedDecl *>
94Sema::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 }
123done:
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.
167static 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
257static 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
300static 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
315namespace {
316
317struct 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.
343static std::optional<unsigned>
344tryParseObjCMethodName(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.
376static std::optional<AttributeInsertion>
377createAttributeInsertion(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.
407static 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
701void 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
713static 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
736namespace {
737
738/// Returns true if the given statement can be a body-like child of \p Parent.
739bool 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
762class StmtUSEFinder : public DynamicRecursiveASTVisitor {
763 const Stmt *Target;
764
765public:
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.
778class LastDeclUSEFinder : public DynamicRecursiveASTVisitor {
779 const Decl *D;
780
781public:
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.
806class 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
817public:
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
877void 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
1006bool 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
1029struct ExtractedAvailabilityExpr {
1030 const ObjCAvailabilityCheckExpr *E = nullptr;
1031 bool isNegated = false;
1032};
1033
1034ExtractedAvailabilityExpr 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
1052bool 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
1081void 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
1101FunctionScopeInfo *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
1112void 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
1154void 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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of clang/lib/Sema/SemaAvailability.cpp