Coverage Report

Created: 2022-01-18 06:27

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