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/RecursiveASTVisitor.h"
16#include "clang/Basic/DiagnosticSema.h"
17#include "clang/Basic/TargetInfo.h"
18#include "clang/Lex/Preprocessor.h"
19#include "clang/Sema/DelayedDiagnostic.h"
20#include "clang/Sema/ScopeInfo.h"
21#include "clang/Sema/Sema.h"
22
23using namespace clang;
24using namespace sema;
25
26static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
27                                                  const Decl *D) {
28  // Check each AvailabilityAttr to find the one for this platform.
29  for (const auto *A : D->attrs()) {
30    if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) {
31      // FIXME: this is copied from CheckAvailability. We should try to
32      // de-duplicate.
33
34      // Check if this is an App Extension "platform", and if so chop off
35      // the suffix for matching with the actual platform.
36      StringRef ActualPlatform = Avail->getPlatform()->getName();
37      StringRef RealizedPlatform = ActualPlatform;
38      if (Context.getLangOpts().AppExt) {
39        size_t suffix = RealizedPlatform.rfind("_app_extension");
40        if (suffix != StringRef::npos)
41          RealizedPlatform = RealizedPlatform.slice(0, suffix);
42      }
43
44      StringRef TargetPlatform = Context.getTargetInfo().getPlatformName();
45
46      // Match the platform name.
47      if (RealizedPlatform == TargetPlatform)
48        return Avail;
49    }
50  }
51  return nullptr;
52}
53
54/// The diagnostic we should emit for \c D, and the declaration that
55/// originated it, or \c AR_Available.
56///
57/// \param D The declaration to check.
58/// \param Message If non-null, this will be populated with the message from
59/// the availability attribute that is selected.
60/// \param ClassReceiver If we're checking the the method of a class message
61/// send, the class. Otherwise nullptr.
62static std::pair<AvailabilityResult, const NamedDecl *>
63ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
64                                 std::string *Message,
65                                 ObjCInterfaceDecl *ClassReceiver) {
66  AvailabilityResult Result = D->getAvailability(Message);
67
68  // For typedefs, if the typedef declaration appears available look
69  // to the underlying type to see if it is more restrictive.
70  while (const auto *TD = dyn_cast<TypedefNameDecl>(D)) {
71    if (Result == AR_Available) {
72      if (const auto *TT = TD->getUnderlyingType()->getAs<TagType>()) {
73        D = TT->getDecl();
74        Result = D->getAvailability(Message);
75        continue;
76      }
77    }
78    break;
79  }
80
81  // Forward class declarations get their attributes from their definition.
82  if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) {
83    if (IDecl->getDefinition()) {
84      D = IDecl->getDefinition();
85      Result = D->getAvailability(Message);
86    }
87  }
88
89  if (const auto *ECD = dyn_cast<EnumConstantDecl>(D))
90    if (Result == AR_Available) {
91      const DeclContext *DC = ECD->getDeclContext();
92      if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) {
93        Result = TheEnumDecl->getAvailability(Message);
94        D = TheEnumDecl;
95      }
96    }
97
98  // For +new, infer availability from -init.
99  if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
100    if (S.NSAPIObj && ClassReceiver) {
101      ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod(
102          S.NSAPIObj->getInitSelector());
103      if (Init && Result == AR_Available && MD->isClassMethod() &&
104          MD->getSelector() == S.NSAPIObj->getNewSelector() &&
105          MD->definedInNSObject(S.getASTContext())) {
106        Result = Init->getAvailability(Message);
107        D = Init;
108      }
109    }
110  }
111
112  return {Result, D};
113}
114
115
116/// whether we should emit a diagnostic for \c K and \c DeclVersion in
117/// the context of \c Ctx. For example, we should emit an unavailable diagnostic
118/// in a deprecated context, but not the other way around.
119static bool
120ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K,
121                                    VersionTuple DeclVersion, Decl *Ctx,
122                                    const NamedDecl *OffendingDecl) {
123  assert(K != AR_Available && "Expected an unavailable declaration here!");
124
125  // Checks if we should emit the availability diagnostic in the context of C.
126  auto CheckContext = [&](const Decl *C) {
127    if (K == AR_NotYetIntroduced) {
128      if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C))
129        if (AA->getIntroduced() >= DeclVersion)
130          return true;
131    } else if (K == AR_Deprecated) {
132      if (C->isDeprecated())
133        return true;
134    } else if (K == AR_Unavailable) {
135      // It is perfectly fine to refer to an 'unavailable' Objective-C method
136      // when it is referenced from within the @implementation itself. In this
137      // context, we interpret unavailable as a form of access control.
138      if (const auto *MD = dyn_cast<ObjCMethodDecl>(OffendingDecl)) {
139        if (const auto *Impl = dyn_cast<ObjCImplDecl>(C)) {
140          if (MD->getClassInterface() == Impl->getClassInterface())
141            return true;
142        }
143      }
144    }
145
146    if (C->isUnavailable())
147      return true;
148    return false;
149  };
150
151  do {
152    if (CheckContext(Ctx))
153      return false;
154
155    // An implementation implicitly has the availability of the interface.
156    // Unless it is "+load" method.
157    if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Ctx))
158      if (MethodD->isClassMethod() &&
159          MethodD->getSelector().getAsString() == "load")
160        return true;
161
162    if (const auto *CatOrImpl = dyn_cast<ObjCImplDecl>(Ctx)) {
163      if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface())
164        if (CheckContext(Interface))
165          return false;
166    }
167    // A category implicitly has the availability of the interface.
168    else if (const auto *CatD = dyn_cast<ObjCCategoryDecl>(Ctx))
169      if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface())
170        if (CheckContext(Interface))
171          return false;
172  } while ((Ctx = cast_or_null<Decl>(Ctx->getDeclContext())));
173
174  return true;
175}
176
177static bool
178shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
179                                    const VersionTuple &DeploymentVersion,
180                                    const VersionTuple &DeclVersion) {
181  const auto &Triple = Context.getTargetInfo().getTriple();
182  VersionTuple ForceAvailabilityFromVersion;
183  switch (Triple.getOS()) {
184  case llvm::Triple::IOS:
185  case llvm::Triple::TvOS:
186    ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11);
187    break;
188  case llvm::Triple::WatchOS:
189    ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4);
190    break;
191  case llvm::Triple::Darwin:
192  case llvm::Triple::MacOSX:
193    ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13);
194    break;
195  default:
196    // New targets should always warn about availability.
197    return Triple.getVendor() == llvm::Triple::Apple;
198  }
199  return DeploymentVersion >= ForceAvailabilityFromVersion ||
200         DeclVersion >= ForceAvailabilityFromVersion;
201}
202
203static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) {
204  for (Decl *Ctx = OrigCtx; Ctx;
205       Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) {
206    if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx))
207      return cast<NamedDecl>(Ctx);
208    if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) {
209      if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx))
210        return Imp->getClassInterface();
211      return CD;
212    }
213  }
214
215  return dyn_cast<NamedDecl>(OrigCtx);
216}
217
218namespace {
219
220struct AttributeInsertion {
221  StringRef Prefix;
222  SourceLocation Loc;
223  StringRef Suffix;
224
225  static AttributeInsertion createInsertionAfter(const NamedDecl *D) {
226    return {" ", D->getEndLoc(), ""};
227  }
228  static AttributeInsertion createInsertionAfter(SourceLocation Loc) {
229    return {" ", Loc, ""};
230  }
231  static AttributeInsertion createInsertionBefore(const NamedDecl *D) {
232    return {"", D->getBeginLoc(), "\n"};
233  }
234};
235
236} // end anonymous namespace
237
238/// Tries to parse a string as ObjC method name.
239///
240/// \param Name The string to parse. Expected to originate from availability
241/// attribute argument.
242/// \param SlotNames The vector that will be populated with slot names. In case
243/// of unsuccessful parsing can contain invalid data.
244/// \returns A number of method parameters if parsing was successful, None
245/// otherwise.
246static Optional<unsigned>
247tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &SlotNames,
248                       const LangOptions &LangOpts) {
249  // Accept replacements starting with - or + as valid ObjC method names.
250  if (!Name.empty() && (Name.front() == '-' || Name.front() == '+'))
251    Name = Name.drop_front(1);
252  if (Name.empty())
253    return None;
254  Name.split(SlotNames, ':');
255  unsigned NumParams;
256  if (Name.back() == ':') {
257    // Remove an empty string at the end that doesn't represent any slot.
258    SlotNames.pop_back();
259    NumParams = SlotNames.size();
260  } else {
261    if (SlotNames.size() != 1)
262      // Not a valid method name, just a colon-separated string.
263      return None;
264    NumParams = 0;
265  }
266  // Verify all slot names are valid.
267  bool AllowDollar = LangOpts.DollarIdents;
268  for (StringRef S : SlotNames) {
269    if (S.empty())
270      continue;
271    if (!isValidIdentifier(S, AllowDollar))
272      return None;
273  }
274  return NumParams;
275}
276
277/// Returns a source location in which it's appropriate to insert a new
278/// attribute for the given declaration \D.
279static Optional<AttributeInsertion>
280createAttributeInsertion(const NamedDecl *D, const SourceManager &SM,
281                         const LangOptions &LangOpts) {
282  if (isa<ObjCPropertyDecl>(D))
283    return AttributeInsertion::createInsertionAfter(D);
284  if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
285    if (MD->hasBody())
286      return None;
287    return AttributeInsertion::createInsertionAfter(D);
288  }
289  if (const auto *TD = dyn_cast<TagDecl>(D)) {
290    SourceLocation Loc =
291        Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 0, SM, LangOpts);
292    if (Loc.isInvalid())
293      return None;
294    // Insert after the 'struct'/whatever keyword.
295    return AttributeInsertion::createInsertionAfter(Loc);
296  }
297  return AttributeInsertion::createInsertionBefore(D);
298}
299
300/// Actually emit an availability diagnostic for a reference to an unavailable
301/// decl.
302///
303/// \param Ctx The context that the reference occurred in
304/// \param ReferringDecl The exact declaration that was referenced.
305/// \param OffendingDecl A related decl to \c ReferringDecl that has an
306/// availability attribute corresponding to \c K attached to it. Note that this
307/// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and
308/// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl
309/// and OffendingDecl is the EnumDecl.
310static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
311                                      Decl *Ctx, const NamedDecl *ReferringDecl,
312                                      const NamedDecl *OffendingDecl,
313                                      StringRef Message,
314                                      ArrayRef<SourceLocation> Locs,
315                                      const ObjCInterfaceDecl *UnknownObjCClass,
316                                      const ObjCPropertyDecl *ObjCProperty,
317                                      bool ObjCPropertyAccess) {
318  // Diagnostics for deprecated or unavailable.
319  unsigned diag, diag_message, diag_fwdclass_message;
320  unsigned diag_available_here = diag::note_availability_specified_here;
321  SourceLocation NoteLocation = OffendingDecl->getLocation();
322
323  // Matches 'diag::note_property_attribute' options.
324  unsigned property_note_select;
325
326  // Matches diag::note_availability_specified_here.
327  unsigned available_here_select_kind;
328
329  VersionTuple DeclVersion;
330  if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl))
331    DeclVersion = AA->getIntroduced();
332
333  if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx,
334                                           OffendingDecl))
335    return;
336
337  SourceLocation Loc = Locs.front();
338
339  // The declaration can have multiple availability attributes, we are looking
340  // at one of them.
341  const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl);
342  if (A && A->isInherited()) {
343    for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl;
344         Redecl = Redecl->getPreviousDecl()) {
345      const AvailabilityAttr *AForRedecl =
346          getAttrForPlatform(S.Context, Redecl);
347      if (AForRedecl && !AForRedecl->isInherited()) {
348        // If D is a declaration with inherited attributes, the note should
349        // point to the declaration with actual attributes.
350        NoteLocation = Redecl->getLocation();
351        break;
352      }
353    }
354  }
355
356  switch (K) {
357  case AR_NotYetIntroduced: {
358    // We would like to emit the diagnostic even if -Wunguarded-availability is
359    // not specified for deployment targets >= to iOS 11 or equivalent or
360    // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
361    // later.
362    const AvailabilityAttr *AA =
363        getAttrForPlatform(S.getASTContext(), OffendingDecl);
364    VersionTuple Introduced = AA->getIntroduced();
365
366    bool UseNewWarning = shouldDiagnoseAvailabilityByDefault(
367        S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
368        Introduced);
369    unsigned Warning = UseNewWarning ? diag::warn_unguarded_availability_new
370                                     : diag::warn_unguarded_availability;
371
372    std::string PlatformName(AvailabilityAttr::getPrettyPlatformName(
373        S.getASTContext().getTargetInfo().getPlatformName()));
374
375    S.Diag(Loc, Warning) << OffendingDecl << PlatformName
376                         << Introduced.getAsString();
377
378    S.Diag(OffendingDecl->getLocation(),
379           diag::note_partial_availability_specified_here)
380        << OffendingDecl << PlatformName << Introduced.getAsString()
381        << S.Context.getTargetInfo().getPlatformMinVersion().getAsString();
382
383    if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) {
384      if (const auto *TD = dyn_cast<TagDecl>(Enclosing))
385        if (TD->getDeclName().isEmpty()) {
386          S.Diag(TD->getLocation(),
387                 diag::note_decl_unguarded_availability_silence)
388              << /*Anonymous*/ 1 << TD->getKindName();
389          return;
390        }
391      auto FixitNoteDiag =
392          S.Diag(Enclosing->getLocation(),
393                 diag::note_decl_unguarded_availability_silence)
394          << /*Named*/ 0 << Enclosing;
395      // Don't offer a fixit for declarations with availability attributes.
396      if (Enclosing->hasAttr<AvailabilityAttr>())
397        return;
398      if (!S.getPreprocessor().isMacroDefined("API_AVAILABLE"))
399        return;
400      Optional<AttributeInsertion> Insertion = createAttributeInsertion(
401          Enclosing, S.getSourceManager(), S.getLangOpts());
402      if (!Insertion)
403        return;
404      std::string PlatformName =
405          AvailabilityAttr::getPlatformNameSourceSpelling(
406              S.getASTContext().getTargetInfo().getPlatformName())
407              .lower();
408      std::string Introduced =
409          OffendingDecl->getVersionIntroduced().getAsString();
410      FixitNoteDiag << FixItHint::CreateInsertion(
411          Insertion->Loc,
412          (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" + PlatformName +
413           "(" + Introduced + "))" + Insertion->Suffix)
414              .str());
415    }
416    return;
417  }
418  case AR_Deprecated:
419    diag = !ObjCPropertyAccess ? diag::warn_deprecated
420                               : diag::warn_property_method_deprecated;
421    diag_message = diag::warn_deprecated_message;
422    diag_fwdclass_message = diag::warn_deprecated_fwdclass_message;
423    property_note_select = /* deprecated */ 0;
424    available_here_select_kind = /* deprecated */ 2;
425    if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>())
426      NoteLocation = AL->getLocation();
427    break;
428
429  case AR_Unavailable:
430    diag = !ObjCPropertyAccess ? diag::err_unavailable
431                               : diag::err_property_method_unavailable;
432    diag_message = diag::err_unavailable_message;
433    diag_fwdclass_message = diag::warn_unavailable_fwdclass_message;
434    property_note_select = /* unavailable */ 1;
435    available_here_select_kind = /* unavailable */ 0;
436
437    if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) {
438      if (AL->isImplicit() && AL->getImplicitReason()) {
439        // Most of these failures are due to extra restrictions in ARC;
440        // reflect that in the primary diagnostic when applicable.
441        auto flagARCError = [&] {
442          if (S.getLangOpts().ObjCAutoRefCount &&
443              S.getSourceManager().isInSystemHeader(
444                  OffendingDecl->getLocation()))
445            diag = diag::err_unavailable_in_arc;
446        };
447
448        switch (AL->getImplicitReason()) {
449        case UnavailableAttr::IR_None: break;
450
451        case UnavailableAttr::IR_ARCForbiddenType:
452          flagARCError();
453          diag_available_here = diag::note_arc_forbidden_type;
454          break;
455
456        case UnavailableAttr::IR_ForbiddenWeak:
457          if (S.getLangOpts().ObjCWeakRuntime)
458            diag_available_here = diag::note_arc_weak_disabled;
459          else
460            diag_available_here = diag::note_arc_weak_no_runtime;
461          break;
462
463        case UnavailableAttr::IR_ARCForbiddenConversion:
464          flagARCError();
465          diag_available_here = diag::note_performs_forbidden_arc_conversion;
466          break;
467
468        case UnavailableAttr::IR_ARCInitReturnsUnrelated:
469          flagARCError();
470          diag_available_here = diag::note_arc_init_returns_unrelated;
471          break;
472
473        case UnavailableAttr::IR_ARCFieldWithOwnership:
474          flagARCError();
475          diag_available_here = diag::note_arc_field_with_ownership;
476          break;
477        }
478      }
479    }
480    break;
481
482  case AR_Available:
483    llvm_unreachable("Warning for availability of available declaration?");
484  }
485
486  SmallVector<FixItHint, 12> FixIts;
487  if (K == AR_Deprecated) {
488    StringRef Replacement;
489    if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>())
490      Replacement = AL->getReplacement();
491    if (auto AL = getAttrForPlatform(S.Context, OffendingDecl))
492      Replacement = AL->getReplacement();
493
494    CharSourceRange UseRange;
495    if (!Replacement.empty())
496      UseRange =
497          CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc));
498    if (UseRange.isValid()) {
499      if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(ReferringDecl)) {
500        Selector Sel = MethodDecl->getSelector();
501        SmallVector<StringRef, 12> SelectorSlotNames;
502        Optional<unsigned> NumParams = tryParseObjCMethodName(
503            Replacement, SelectorSlotNames, S.getLangOpts());
504        if (NumParams && NumParams.getValue() == Sel.getNumArgs()) {
505          assert(SelectorSlotNames.size() == Locs.size());
506          for (unsigned I = 0; I < Locs.size(); ++I) {
507            if (!Sel.getNameForSlot(I).empty()) {
508              CharSourceRange NameRange = CharSourceRange::getCharRange(
509                  Locs[I], S.getLocForEndOfToken(Locs[I]));
510              FixIts.push_back(FixItHint::CreateReplacement(
511                  NameRange, SelectorSlotNames[I]));
512            } else
513              FixIts.push_back(
514                  FixItHint::CreateInsertion(Locs[I], SelectorSlotNames[I]));
515          }
516        } else
517          FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));
518      } else
519        FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));
520    }
521  }
522
523  if (!Message.empty()) {
524    S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts;
525    if (ObjCProperty)
526      S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
527          << ObjCProperty->getDeclName() << property_note_select;
528  } else if (!UnknownObjCClass) {
529    S.Diag(Loc, diag) << ReferringDecl << FixIts;
530    if (ObjCProperty)
531      S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
532          << ObjCProperty->getDeclName() << property_note_select;
533  } else {
534    S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts;
535    S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class);
536  }
537
538  S.Diag(NoteLocation, diag_available_here)
539    << OffendingDecl << available_here_select_kind;
540}
541
542void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) {
543  assert(DD.Kind == DelayedDiagnostic::Availability &&
544         "Expected an availability diagnostic here");
545
546  DD.Triggered = true;
547  DoEmitAvailabilityWarning(
548      *this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(),
549      DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(),
550      DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(),
551      DD.getObjCProperty(), false);
552}
553
554static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR,
555                                    const NamedDecl *ReferringDecl,
556                                    const NamedDecl *OffendingDecl,
557                                    StringRef Message,
558                                    ArrayRef<SourceLocation> Locs,
559                                    const ObjCInterfaceDecl *UnknownObjCClass,
560                                    const ObjCPropertyDecl *ObjCProperty,
561                                    bool ObjCPropertyAccess) {
562  // Delay if we're currently parsing a declaration.
563  if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
564    S.DelayedDiagnostics.add(
565        DelayedDiagnostic::makeAvailability(
566            AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass,
567            ObjCProperty, Message, ObjCPropertyAccess));
568    return;
569  }
570
571  Decl *Ctx = cast<Decl>(S.getCurLexicalContext());
572  DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl,
573                            Message, Locs, UnknownObjCClass, ObjCProperty,
574                            ObjCPropertyAccess);
575}
576
577namespace {
578
579/// Returns true if the given statement can be a body-like child of \p Parent.
580bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) {
581  switch (Parent->getStmtClass()) {
582  case Stmt::IfStmtClass:
583    return cast<IfStmt>(Parent)->getThen() == S ||
584           cast<IfStmt>(Parent)->getElse() == S;
585  case Stmt::WhileStmtClass:
586    return cast<WhileStmt>(Parent)->getBody() == S;
587  case Stmt::DoStmtClass:
588    return cast<DoStmt>(Parent)->getBody() == S;
589  case Stmt::ForStmtClass:
590    return cast<ForStmt>(Parent)->getBody() == S;
591  case Stmt::CXXForRangeStmtClass:
592    return cast<CXXForRangeStmt>(Parent)->getBody() == S;
593  case Stmt::ObjCForCollectionStmtClass:
594    return cast<ObjCForCollectionStmt>(Parent)->getBody() == S;
595  case Stmt::CaseStmtClass:
596  case Stmt::DefaultStmtClass:
597    return cast<SwitchCase>(Parent)->getSubStmt() == S;
598  default:
599    return false;
600  }
601}
602
603class StmtUSEFinder : public RecursiveASTVisitor<StmtUSEFinder> {
604  const Stmt *Target;
605
606public:
607  bool VisitStmt(Stmt *S) { return S != Target; }
608
609  /// Returns true if the given statement is present in the given declaration.
610  static bool isContained(const Stmt *Target, const Decl *D) {
611    StmtUSEFinder Visitor;
612    Visitor.Target = Target;
613    return !Visitor.TraverseDecl(const_cast<Decl *>(D));
614  }
615};
616
617/// Traverses the AST and finds the last statement that used a given
618/// declaration.
619class LastDeclUSEFinder : public RecursiveASTVisitor<LastDeclUSEFinder> {
620  const Decl *D;
621
622public:
623  bool VisitDeclRefExpr(DeclRefExpr *DRE) {
624    if (DRE->getDecl() == D)
625      return false;
626    return true;
627  }
628
629  static const Stmt *findLastStmtThatUsesDecl(const Decl *D,
630                                              const CompoundStmt *Scope) {
631    LastDeclUSEFinder Visitor;
632    Visitor.D = D;
633    for (auto I = Scope->body_rbegin(), E = Scope->body_rend(); I != E; ++I) {
634      const Stmt *S = *I;
635      if (!Visitor.TraverseStmt(const_cast<Stmt *>(S)))
636        return S;
637    }
638    return nullptr;
639  }
640};
641
642/// This class implements -Wunguarded-availability.
643///
644/// This is done with a traversal of the AST of a function that makes reference
645/// to a partially available declaration. Whenever we encounter an \c if of the
646/// form: \c if(@available(...)), we use the version from the condition to visit
647/// the then statement.
648class DiagnoseUnguardedAvailability
649    : public RecursiveASTVisitor<DiagnoseUnguardedAvailability> {
650  typedef RecursiveASTVisitor<DiagnoseUnguardedAvailability> Base;
651
652  Sema &SemaRef;
653  Decl *Ctx;
654
655  /// Stack of potentially nested 'if (@available(...))'s.
656  SmallVector<VersionTuple, 8> AvailabilityStack;
657  SmallVector<const Stmt *, 16> StmtStack;
658
659  void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range,
660                                ObjCInterfaceDecl *ClassReceiver = nullptr);
661
662public:
663  DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx)
664      : SemaRef(SemaRef), Ctx(Ctx) {
665    AvailabilityStack.push_back(
666        SemaRef.Context.getTargetInfo().getPlatformMinVersion());
667  }
668
669  bool TraverseDecl(Decl *D) {
670    // Avoid visiting nested functions to prevent duplicate warnings.
671    if (!D || isa<FunctionDecl>(D))
672      return true;
673    return Base::TraverseDecl(D);
674  }
675
676  bool TraverseStmt(Stmt *S) {
677    if (!S)
678      return true;
679    StmtStack.push_back(S);
680    bool Result = Base::TraverseStmt(S);
681    StmtStack.pop_back();
682    return Result;
683  }
684
685  void IssueDiagnostics(Stmt *S) { TraverseStmt(S); }
686
687  bool TraverseIfStmt(IfStmt *If);
688
689  bool TraverseLambdaExpr(LambdaExpr *E) { return true; }
690
691  // for 'case X:' statements, don't bother looking at the 'X'; it can't lead
692  // to any useful diagnostics.
693  bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); }
694
695  bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) {
696    if (PRE->isClassReceiver())
697      DiagnoseDeclAvailability(PRE->getClassReceiver(), PRE->getReceiverLocation());
698    return true;
699  }
700
701  bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) {
702    if (ObjCMethodDecl *D = Msg->getMethodDecl()) {
703      ObjCInterfaceDecl *ID = nullptr;
704      QualType ReceiverTy = Msg->getClassReceiver();
705      if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType())
706        ID = ReceiverTy->getAsObjCInterfaceType()->getInterface();
707
708      DiagnoseDeclAvailability(
709          D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID);
710    }
711    return true;
712  }
713
714  bool VisitDeclRefExpr(DeclRefExpr *DRE) {
715    DiagnoseDeclAvailability(DRE->getDecl(),
716                             SourceRange(DRE->getBeginLoc(), DRE->getEndLoc()));
717    return true;
718  }
719
720  bool VisitMemberExpr(MemberExpr *ME) {
721    DiagnoseDeclAvailability(ME->getMemberDecl(),
722                             SourceRange(ME->getBeginLoc(), ME->getEndLoc()));
723    return true;
724  }
725
726  bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) {
727    SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use)
728        << (!SemaRef.getLangOpts().ObjC);
729    return true;
730  }
731
732  bool VisitTypeLoc(TypeLoc Ty);
733};
734
735void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
736    NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) {
737  AvailabilityResult Result;
738  const NamedDecl *OffendingDecl;
739  std::tie(Result, OffendingDecl) =
740      ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass);
741  if (Result != AR_Available) {
742    // All other diagnostic kinds have already been handled in
743    // DiagnoseAvailabilityOfDecl.
744    if (Result != AR_NotYetIntroduced)
745      return;
746
747    const AvailabilityAttr *AA =
748      getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);
749    VersionTuple Introduced = AA->getIntroduced();
750
751    if (AvailabilityStack.back() >= Introduced)
752      return;
753
754    // If the context of this function is less available than D, we should not
755    // emit a diagnostic.
756    if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx,
757                                             OffendingDecl))
758      return;
759
760    // We would like to emit the diagnostic even if -Wunguarded-availability is
761    // not specified for deployment targets >= to iOS 11 or equivalent or
762    // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
763    // later.
764    unsigned DiagKind =
765        shouldDiagnoseAvailabilityByDefault(
766            SemaRef.Context,
767            SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced)
768            ? diag::warn_unguarded_availability_new
769            : diag::warn_unguarded_availability;
770
771    std::string PlatformName(AvailabilityAttr::getPrettyPlatformName(
772        SemaRef.getASTContext().getTargetInfo().getPlatformName()));
773
774    SemaRef.Diag(Range.getBegin(), DiagKind)
775        << Range << D << PlatformName << Introduced.getAsString();
776
777    SemaRef.Diag(OffendingDecl->getLocation(),
778                 diag::note_partial_availability_specified_here)
779        << OffendingDecl << PlatformName << Introduced.getAsString()
780        << SemaRef.Context.getTargetInfo()
781               .getPlatformMinVersion()
782               .getAsString();
783
784    auto FixitDiag =
785        SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
786        << Range << D
787        << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0
788                                       : /*__builtin_available*/ 1);
789
790    // Find the statement which should be enclosed in the if @available check.
791    if (StmtStack.empty())
792      return;
793    const Stmt *StmtOfUse = StmtStack.back();
794    const CompoundStmt *Scope = nullptr;
795    for (const Stmt *S : llvm::reverse(StmtStack)) {
796      if (const auto *CS = dyn_cast<CompoundStmt>(S)) {
797        Scope = CS;
798        break;
799      }
800      if (isBodyLikeChildStmt(StmtOfUse, S)) {
801        // The declaration won't be seen outside of the statement, so we don't
802        // have to wrap the uses of any declared variables in if (@available).
803        // Therefore we can avoid setting Scope here.
804        break;
805      }
806      StmtOfUse = S;
807    }
808    const Stmt *LastStmtOfUse = nullptr;
809    if (isa<DeclStmt>(StmtOfUse) && Scope) {
810      for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) {
811        if (StmtUSEFinder::isContained(StmtStack.back(), D)) {
812          LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope);
813          break;
814        }
815      }
816    }
817
818    const SourceManager &SM = SemaRef.getSourceManager();
819    SourceLocation IfInsertionLoc =
820        SM.getExpansionLoc(StmtOfUse->getBeginLoc());
821    SourceLocation StmtEndLoc =
822        SM.getExpansionRange(
823              (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc())
824            .getEnd();
825    if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc))
826      return;
827
828    StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM);
829    const char *ExtraIndentation = "    ";
830    std::string FixItString;
831    llvm::raw_string_ostream FixItOS(FixItString);
832    FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available"
833                                                     : "__builtin_available")
834            << "("
835            << AvailabilityAttr::getPlatformNameSourceSpelling(
836                   SemaRef.getASTContext().getTargetInfo().getPlatformName())
837            << " " << Introduced.getAsString() << ", *)) {\n"
838            << Indentation << ExtraIndentation;
839    FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str());
840    SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken(
841        StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(),
842        /*SkipTrailingWhitespaceAndNewLine=*/false);
843    if (ElseInsertionLoc.isInvalid())
844      ElseInsertionLoc =
845          Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts());
846    FixItOS.str().clear();
847    FixItOS << "\n"
848            << Indentation << "} else {\n"
849            << Indentation << ExtraIndentation
850            << "// Fallback on earlier versions\n"
851            << Indentation << "}";
852    FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str());
853  }
854}
855
856bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) {
857  const Type *TyPtr = Ty.getTypePtr();
858  SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()};
859
860  if (Range.isInvalid())
861    return true;
862
863  if (const auto *TT = dyn_cast<TagType>(TyPtr)) {
864    TagDecl *TD = TT->getDecl();
865    DiagnoseDeclAvailability(TD, Range);
866
867  } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) {
868    TypedefNameDecl *D = TD->getDecl();
869    DiagnoseDeclAvailability(D, Range);
870
871  } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) {
872    if (NamedDecl *D = ObjCO->getInterface())
873      DiagnoseDeclAvailability(D, Range);
874  }
875
876  return true;
877}
878
879bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
880  VersionTuple CondVersion;
881  if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) {
882    CondVersion = E->getVersion();
883
884    // If we're using the '*' case here or if this check is redundant, then we
885    // use the enclosing version to check both branches.
886    if (CondVersion.empty() || CondVersion <= AvailabilityStack.back())
887      return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
888  } else {
889    // This isn't an availability checking 'if', we can just continue.
890    return Base::TraverseIfStmt(If);
891  }
892
893  AvailabilityStack.push_back(CondVersion);
894  bool ShouldContinue = TraverseStmt(If->getThen());
895  AvailabilityStack.pop_back();
896
897  return ShouldContinue && TraverseStmt(If->getElse());
898}
899
900} // end anonymous namespace
901
902void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) {
903  Stmt *Body = nullptr;
904
905  if (auto *FD = D->getAsFunction()) {
906    // FIXME: We only examine the pattern decl for availability violations now,
907    // but we should also examine instantiated templates.
908    if (FD->isTemplateInstantiation())
909      return;
910
911    Body = FD->getBody();
912  } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D))
913    Body = MD->getBody();
914  else if (auto *BD = dyn_cast<BlockDecl>(D))
915    Body = BD->getBody();
916
917  assert(Body && "Need a body here!");
918
919  DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body);
920}
921
922void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
923                                      ArrayRef<SourceLocation> Locs,
924                                      const ObjCInterfaceDecl *UnknownObjCClass,
925                                      bool ObjCPropertyAccess,
926                                      bool AvoidPartialAvailabilityChecks,
927                                      ObjCInterfaceDecl *ClassReceiver) {
928  std::string Message;
929  AvailabilityResult Result;
930  const NamedDecl* OffendingDecl;
931  // See if this declaration is unavailable, deprecated, or partial.
932  std::tie(Result, OffendingDecl) =
933      ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver);
934  if (Result == AR_Available)
935    return;
936
937  if (Result == AR_NotYetIntroduced) {
938    if (AvoidPartialAvailabilityChecks)
939      return;
940
941    // We need to know the @available context in the current function to
942    // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that
943    // when we're done parsing the current function.
944    if (getCurFunctionOrMethodDecl()) {
945      getEnclosingFunction()->HasPotentialAvailabilityViolations = true;
946      return;
947    } else if (getCurBlock() || getCurLambda()) {
948      getCurFunction()->HasPotentialAvailabilityViolations = true;
949      return;
950    }
951  }
952
953  const ObjCPropertyDecl *ObjCPDecl = nullptr;
954  if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
955    if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
956      AvailabilityResult PDeclResult = PD->getAvailability(nullptr);
957      if (PDeclResult == Result)
958        ObjCPDecl = PD;
959    }
960  }
961
962  EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs,
963                          UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess);
964}
965