Coverage Report

Created: 2024-01-17 10:31

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