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