Coverage Report

Created: 2021-09-21 08:58

/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
327k
                                                  const Decl *D) {
28
  // Check each AvailabilityAttr to find the one for this platform.
29
358k
  for (const auto *A : D->attrs()) {
30
358k
    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
358k
  }
51
16.9k
  return nullptr;
52
327k
}
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
154M
                                 ObjCInterfaceDecl *ClassReceiver) {
66
154M
  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
156M
  while (const auto *TD = dyn_cast<TypedefNameDecl>(D)) {
71
123M
    if (Result == AR_Available) {
72
122M
      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
122M
    }
78
121M
    break;
79
123M
  }
80
81
  // Forward class declarations get their attributes from their definition.
82
154M
  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
154M
  if (const auto *ECD = dyn_cast<EnumConstantDecl>(D))
90
402k
    if (Result == AR_Available) {
91
401k
      const DeclContext *DC = ECD->getDeclContext();
92
401k
      if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) {
93
401k
        Result = TheEnumDecl->getAvailability(Message);
94
401k
        D = TheEnumDecl;
95
401k
      }
96
401k
    }
97
98
  // For +new, infer availability from -init.
99
154M
  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
154M
  return {Result, D};
113
154M
}
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
260k
                                    const NamedDecl *OffendingDecl) {
123
260k
  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
428k
  auto CheckContext = [&](const Decl *C) {
127
428k
    if (K == AR_NotYetIntroduced) {
128
612
      if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C))
129
74
        if (AA->getIntroduced() >= DeclVersion)
130
56
          return true;
131
428k
    } else if (K == AR_Deprecated) {
132
426k
      if (C->isDeprecated())
133
227k
        return true;
134
426k
    } else 
if (1.44k
K == AR_Unavailable1.44k
) {
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.44k
      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.44k
    }
145
146
201k
    if (C->isUnavailable())
147
331
      return true;
148
201k
    return false;
149
201k
  };
150
151
428k
  do {
152
428k
    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
201k
    if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Ctx))
158
2.89k
      if (MethodD->isClassMethod() &&
159
2.89k
          
MethodD->getSelector().getAsString() == "load"903
)
160
4
        return true;
161
162
201k
    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
200k
    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
201k
  } while (
(Ctx = cast_or_null<Decl>(Ctx->getDeclContext()))201k
);
173
174
33.3k
  return true;
175
260k
}
176
177
static bool
178
shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
179
                                    const VersionTuple &DeploymentVersion,
180
229
                                    const VersionTuple &DeclVersion) {
181
229
  const auto &Triple = Context.getTargetInfo().getTriple();
182
229
  VersionTuple ForceAvailabilityFromVersion;
183
229
  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
1
  default:
196
    // New targets should always warn about availability.
197
1
    return Triple.getVendor() == llvm::Triple::Apple;
198
229
  }
199
228
  return DeploymentVersion >= ForceAvailabilityFromVersion ||
200
228
         
DeclVersion >= ForceAvailabilityFromVersion185
;
201
229
}
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
260k
                                      bool ObjCPropertyAccess) {
318
  // Diagnostics for deprecated or unavailable.
319
260k
  unsigned diag, diag_message, diag_fwdclass_message;
320
260k
  unsigned diag_available_here = diag::note_availability_specified_here;
321
260k
  SourceLocation NoteLocation = OffendingDecl->getLocation();
322
323
  // Matches 'diag::note_property_attribute' options.
324
260k
  unsigned property_note_select;
325
326
  // Matches diag::note_availability_specified_here.
327
260k
  unsigned available_here_select_kind;
328
329
260k
  VersionTuple DeclVersion;
330
260k
  if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl))
331
254k
    DeclVersion = AA->getIntroduced();
332
333
260k
  if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx,
334
260k
                                           OffendingDecl))
335
227k
    return;
336
337
33.1k
  SourceLocation Loc = Locs.front();
338
339
  // The declaration can have multiple availability attributes, we are looking
340
  // at one of them.
341
33.1k
  const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl);
342
33.1k
  if (A && 
A->isInherited()27.8k
) {
343
14
    for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl;
344
14
         
Redecl = Redecl->getPreviousDecl()7
) {
345
14
      const AvailabilityAttr *AForRedecl =
346
14
          getAttrForPlatform(S.Context, Redecl);
347
14
      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
7
        NoteLocation = Redecl->getLocation();
351
7
        break;
352
7
      }
353
14
    }
354
7
  }
355
356
33.1k
  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
32.7k
  case AR_Deprecated:
419
32.7k
    diag = !ObjCPropertyAccess ? 
diag::warn_deprecated32.6k
420
32.7k
                               : 
diag::warn_property_method_deprecated9
;
421
32.7k
    diag_message = diag::warn_deprecated_message;
422
32.7k
    diag_fwdclass_message = diag::warn_deprecated_fwdclass_message;
423
32.7k
    property_note_select = /* deprecated */ 0;
424
32.7k
    available_here_select_kind = /* deprecated */ 2;
425
32.7k
    if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>())
426
4.96k
      NoteLocation = AL->getLocation();
427
32.7k
    break;
428
429
397
  case AR_Unavailable:
430
397
    diag = !ObjCPropertyAccess ? 
diag::err_unavailable394
431
397
                               : 
diag::err_property_method_unavailable3
;
432
397
    diag_message = diag::err_unavailable_message;
433
397
    diag_fwdclass_message = diag::warn_unavailable_fwdclass_message;
434
397
    property_note_select = /* unavailable */ 1;
435
397
    available_here_select_kind = /* unavailable */ 0;
436
437
397
    if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) {
438
321
      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
321
    }
480
397
    break;
481
482
397
  case AR_Available:
483
0
    llvm_unreachable("Warning for availability of available declaration?");
484
33.1k
  }
485
486
33.1k
  SmallVector<FixItHint, 12> FixIts;
487
33.1k
  if (K == AR_Deprecated) {
488
32.7k
    StringRef Replacement;
489
32.7k
    if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>())
490
4.96k
      Replacement = AL->getReplacement();
491
32.7k
    if (auto AL = getAttrForPlatform(S.Context, OffendingDecl))
492
27.7k
      Replacement = AL->getReplacement();
493
494
32.7k
    CharSourceRange UseRange;
495
32.7k
    if (!Replacement.empty())
496
53
      UseRange =
497
53
          CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc));
498
32.7k
    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
32.7k
  }
522
523
33.1k
  if (!Message.empty()) {
524
32.4k
    S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts;
525
32.4k
    if (ObjCProperty)
526
28
      S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
527
28
          << ObjCProperty->getDeclName() << property_note_select;
528
32.4k
  } else 
if (632
!UnknownObjCClass632
) {
529
621
    S.Diag(Loc, diag) << ReferringDecl << FixIts;
530
621
    if (ObjCProperty)
531
6
      S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
532
6
          << ObjCProperty->getDeclName() << property_note_select;
533
621
  } 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
33.1k
  S.Diag(NoteLocation, diag_available_here)
539
33.1k
    << OffendingDecl << available_here_select_kind;
540
33.1k
}
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
260k
                                    bool ObjCPropertyAccess) {
562
  // Delay if we're currently parsing a declaration.
563
260k
  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
6.45k
  Decl *Ctx = cast<Decl>(S.getCurLexicalContext());
572
6.45k
  DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl,
573
6.45k
                            Message, Locs, UnknownObjCClass, ObjCProperty,
574
6.45k
                            ObjCPropertyAccess);
575
6.45k
}
576
577
namespace {
578
579
/// Returns true if the given statement can be a body-like child of \p Parent.
580
464
bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) {
581
464
  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
439
  default:
599
439
    return false;
600
464
  }
601
464
}
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
39
    for (auto I = Scope->body_rbegin(), E = Scope->body_rend(); I != E; 
++I25
) {
634
31
      const Stmt *S = *I;
635
31
      if (!Visitor.TraverseStmt(const_cast<Stmt *>(S)))
636
6
        return S;
637
31
    }
638
8
    return nullptr;
639
14
  }
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.
648
class 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
662
public:
663
  DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx)
664
145
      : SemaRef(SemaRef), Ctx(Ctx) {
665
145
    AvailabilityStack.push_back(
666
145
        SemaRef.Context.getTargetInfo().getPlatformMinVersion());
667
145
  }
668
669
2.02k
  bool TraverseStmt(Stmt *S) {
670
2.02k
    if (!S)
671
88
      return true;
672
1.93k
    StmtStack.push_back(S);
673
1.93k
    bool Result = Base::TraverseStmt(S);
674
1.93k
    StmtStack.pop_back();
675
1.93k
    return Result;
676
2.02k
  }
677
678
145
  void IssueDiagnostics(Stmt *S) { TraverseStmt(S); }
679
680
  bool TraverseIfStmt(IfStmt *If);
681
682
  // for 'case X:' statements, don't bother looking at the 'X'; it can't lead
683
  // to any useful diagnostics.
684
12
  bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); }
685
686
9
  bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) { return true; }
687
688
130
  bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) {
689
130
    if (ObjCMethodDecl *D = Msg->getMethodDecl()) {
690
128
      ObjCInterfaceDecl *ID = nullptr;
691
128
      QualType ReceiverTy = Msg->getClassReceiver();
692
128
      if (!ReceiverTy.isNull() && 
ReceiverTy->getAsObjCInterfaceType()15
)
693
15
        ID = ReceiverTy->getAsObjCInterfaceType()->getInterface();
694
695
128
      DiagnoseDeclAvailability(
696
128
          D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID);
697
128
    }
698
130
    return true;
699
130
  }
700
701
426
  bool VisitDeclRefExpr(DeclRefExpr *DRE) {
702
426
    DiagnoseDeclAvailability(DRE->getDecl(),
703
426
                             SourceRange(DRE->getBeginLoc(), DRE->getEndLoc()));
704
426
    return true;
705
426
  }
706
707
8
  bool VisitMemberExpr(MemberExpr *ME) {
708
8
    DiagnoseDeclAvailability(ME->getMemberDecl(),
709
8
                             SourceRange(ME->getBeginLoc(), ME->getEndLoc()));
710
8
    return true;
711
8
  }
712
713
9
  bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) {
714
9
    SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use)
715
9
        << (!SemaRef.getLangOpts().ObjC);
716
9
    return true;
717
9
  }
718
719
  bool VisitTypeLoc(TypeLoc Ty);
720
};
721
722
void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
723
619
    NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) {
724
619
  AvailabilityResult Result;
725
619
  const NamedDecl *OffendingDecl;
726
619
  std::tie(Result, OffendingDecl) =
727
619
      ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass);
728
619
  if (Result != AR_Available) {
729
    // All other diagnostic kinds have already been handled in
730
    // DiagnoseAvailabilityOfDecl.
731
299
    if (Result != AR_NotYetIntroduced)
732
46
      return;
733
734
253
    const AvailabilityAttr *AA =
735
253
      getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);
736
253
    VersionTuple Introduced = AA->getIntroduced();
737
738
253
    if (AvailabilityStack.back() >= Introduced)
739
62
      return;
740
741
    // If the context of this function is less available than D, we should not
742
    // emit a diagnostic.
743
191
    if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx,
744
191
                                             OffendingDecl))
745
17
      return;
746
747
    // We would like to emit the diagnostic even if -Wunguarded-availability is
748
    // not specified for deployment targets >= to iOS 11 or equivalent or
749
    // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
750
    // later.
751
174
    unsigned DiagKind =
752
174
        shouldDiagnoseAvailabilityByDefault(
753
174
            SemaRef.Context,
754
174
            SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced)
755
174
            ? 
diag::warn_unguarded_availability_new37
756
174
            : 
diag::warn_unguarded_availability137
;
757
758
174
    std::string PlatformName(AvailabilityAttr::getPrettyPlatformName(
759
174
        SemaRef.getASTContext().getTargetInfo().getPlatformName()));
760
761
174
    SemaRef.Diag(Range.getBegin(), DiagKind)
762
174
        << Range << D << PlatformName << Introduced.getAsString();
763
764
174
    SemaRef.Diag(OffendingDecl->getLocation(),
765
174
                 diag::note_partial_availability_specified_here)
766
174
        << OffendingDecl << PlatformName << Introduced.getAsString()
767
174
        << SemaRef.Context.getTargetInfo()
768
174
               .getPlatformMinVersion()
769
174
               .getAsString();
770
771
174
    auto FixitDiag =
772
174
        SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
773
174
        << Range << D
774
174
        << (SemaRef.getLangOpts().ObjC ? /*@available*/ 
0159
775
174
                                       : /*__builtin_available*/ 
115
);
776
777
    // Find the statement which should be enclosed in the if @available check.
778
174
    if (StmtStack.empty())
779
0
      return;
780
174
    const Stmt *StmtOfUse = StmtStack.back();
781
174
    const CompoundStmt *Scope = nullptr;
782
618
    for (const Stmt *S : llvm::reverse(StmtStack)) {
783
618
      if (const auto *CS = dyn_cast<CompoundStmt>(S)) {
784
154
        Scope = CS;
785
154
        break;
786
154
      }
787
464
      if (isBodyLikeChildStmt(StmtOfUse, S)) {
788
        // The declaration won't be seen outside of the statement, so we don't
789
        // have to wrap the uses of any declared variables in if (@available).
790
        // Therefore we can avoid setting Scope here.
791
20
        break;
792
20
      }
793
444
      StmtOfUse = S;
794
444
    }
795
174
    const Stmt *LastStmtOfUse = nullptr;
796
174
    if (isa<DeclStmt>(StmtOfUse) && 
Scope23
) {
797
23
      for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) {
798
23
        if (StmtUSEFinder::isContained(StmtStack.back(), D)) {
799
14
          LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope);
800
14
          break;
801
14
        }
802
23
      }
803
22
    }
804
805
174
    const SourceManager &SM = SemaRef.getSourceManager();
806
174
    SourceLocation IfInsertionLoc =
807
174
        SM.getExpansionLoc(StmtOfUse->getBeginLoc());
808
174
    SourceLocation StmtEndLoc =
809
174
        SM.getExpansionRange(
810
174
              (LastStmtOfUse ? 
LastStmtOfUse6
:
StmtOfUse168
)->getEndLoc())
811
174
            .getEnd();
812
174
    if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc))
813
0
      return;
814
815
174
    StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM);
816
174
    const char *ExtraIndentation = "    ";
817
174
    std::string FixItString;
818
174
    llvm::raw_string_ostream FixItOS(FixItString);
819
174
    FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? 
"@available"159
820
174
                                                     : 
"__builtin_available"15
)
821
174
            << "("
822
174
            << AvailabilityAttr::getPlatformNameSourceSpelling(
823
174
                   SemaRef.getASTContext().getTargetInfo().getPlatformName())
824
174
            << " " << Introduced.getAsString() << ", *)) {\n"
825
174
            << Indentation << ExtraIndentation;
826
174
    FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str());
827
174
    SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken(
828
174
        StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(),
829
174
        /*SkipTrailingWhitespaceAndNewLine=*/false);
830
174
    if (ElseInsertionLoc.isInvalid())
831
22
      ElseInsertionLoc =
832
22
          Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts());
833
174
    FixItOS.str().clear();
834
174
    FixItOS << "\n"
835
174
            << Indentation << "} else {\n"
836
174
            << Indentation << ExtraIndentation
837
174
            << "// Fallback on earlier versions\n"
838
174
            << Indentation << "}";
839
174
    FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str());
840
174
  }
841
619
}
842
843
134
bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) {
844
134
  const Type *TyPtr = Ty.getTypePtr();
845
134
  SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()};
846
847
134
  if (Range.isInvalid())
848
11
    return true;
849
850
123
  if (const auto *TT = dyn_cast<TagType>(TyPtr)) {
851
4
    TagDecl *TD = TT->getDecl();
852
4
    DiagnoseDeclAvailability(TD, Range);
853
854
119
  } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) {
855
34
    TypedefNameDecl *D = TD->getDecl();
856
34
    DiagnoseDeclAvailability(D, Range);
857
858
85
  } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) {
859
19
    if (NamedDecl *D = ObjCO->getInterface())
860
19
      DiagnoseDeclAvailability(D, Range);
861
19
  }
862
863
123
  return true;
864
134
}
865
866
118
bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
867
118
  VersionTuple CondVersion;
868
118
  if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) {
869
98
    CondVersion = E->getVersion();
870
871
    // If we're using the '*' case here or if this check is redundant, then we
872
    // use the enclosing version to check both branches.
873
98
    if (CondVersion.empty() || 
CondVersion <= AvailabilityStack.back()87
)
874
26
      return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
875
98
  } else {
876
    // This isn't an availability checking 'if', we can just continue.
877
20
    return Base::TraverseIfStmt(If);
878
20
  }
879
880
72
  AvailabilityStack.push_back(CondVersion);
881
72
  bool ShouldContinue = TraverseStmt(If->getThen());
882
72
  AvailabilityStack.pop_back();
883
884
72
  return ShouldContinue && TraverseStmt(If->getElse());
885
118
}
886
887
} // end anonymous namespace
888
889
146
void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) {
890
146
  Stmt *Body = nullptr;
891
892
146
  if (auto *FD = D->getAsFunction()) {
893
    // FIXME: We only examine the pattern decl for availability violations now,
894
    // but we should also examine instantiated templates.
895
122
    if (FD->isTemplateInstantiation())
896
1
      return;
897
898
121
    Body = FD->getBody();
899
121
  } else 
if (auto *24
MD24
= dyn_cast<ObjCMethodDecl>(D))
900
22
    Body = MD->getBody();
901
2
  else if (auto *BD = dyn_cast<BlockDecl>(D))
902
2
    Body = BD->getBody();
903
904
145
  assert(Body && "Need a body here!");
905
906
0
  DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body);
907
145
}
908
909
471
FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() {
910
471
  if (FunctionScopes.empty())
911
95
    return nullptr;
912
913
  // Conservatively search the entire current function scope context for
914
  // availability violations. This ensures we always correctly analyze nested
915
  // classes, blocks, lambdas, etc. that may or may not be inside if(@available)
916
  // checks themselves.
917
376
  return FunctionScopes.front();
918
471
}
919
920
void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
921
                                      ArrayRef<SourceLocation> Locs,
922
                                      const ObjCInterfaceDecl *UnknownObjCClass,
923
                                      bool ObjCPropertyAccess,
924
                                      bool AvoidPartialAvailabilityChecks,
925
154M
                                      ObjCInterfaceDecl *ClassReceiver) {
926
154M
  std::string Message;
927
154M
  AvailabilityResult Result;
928
154M
  const NamedDecl* OffendingDecl;
929
  // See if this declaration is unavailable, deprecated, or partial.
930
154M
  std::tie(Result, OffendingDecl) =
931
154M
      ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver);
932
154M
  if (Result == AR_Available)
933
154M
    return;
934
935
260k
  if (Result == AR_NotYetIntroduced) {
936
369
    if (AvoidPartialAvailabilityChecks)
937
6
      return;
938
939
    // We need to know the @available context in the current function to
940
    // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that
941
    // when we're done parsing the current function.
942
363
    if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) {
943
269
      Context->HasPotentialAvailabilityViolations = true;
944
269
      return;
945
269
    }
946
363
  }
947
948
260k
  const ObjCPropertyDecl *ObjCPDecl = nullptr;
949
260k
  if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
950
424
    if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
951
37
      AvailabilityResult PDeclResult = PD->getAvailability(nullptr);
952
37
      if (PDeclResult == Result)
953
34
        ObjCPDecl = PD;
954
37
    }
955
424
  }
956
957
260k
  EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs,
958
260k
                          UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess);
959
260k
}