1 //===--- SemaAvailability.cpp - Availability attribute handling -----------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file processes the availability attribute.
11 //===----------------------------------------------------------------------===//
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"
24 using namespace clang
;
27 static const AvailabilityAttr
*getAttrForPlatform(ASTContext
&Context
,
29 // Check each AvailabilityAttr to find the one for this platform.
30 for (const auto *A
: D
->attrs()) {
31 if (const auto *Avail
= dyn_cast
<AvailabilityAttr
>(A
)) {
32 // FIXME: this is copied from CheckAvailability. We should try to
35 // Check if this is an App Extension "platform", and if so chop off
36 // the suffix for matching with the actual platform.
37 StringRef ActualPlatform
= Avail
->getPlatform()->getName();
38 StringRef RealizedPlatform
= ActualPlatform
;
39 if (Context
.getLangOpts().AppExt
) {
40 size_t suffix
= RealizedPlatform
.rfind("_app_extension");
41 if (suffix
!= StringRef::npos
)
42 RealizedPlatform
= RealizedPlatform
.slice(0, suffix
);
45 StringRef TargetPlatform
= Context
.getTargetInfo().getPlatformName();
47 // Match the platform name.
48 if (RealizedPlatform
== TargetPlatform
)
55 /// The diagnostic we should emit for \c D, and the declaration that
56 /// originated it, or \c AR_Available.
58 /// \param D The declaration to check.
59 /// \param Message If non-null, this will be populated with the message from
60 /// the availability attribute that is selected.
61 /// \param ClassReceiver If we're checking the method of a class message
62 /// send, the class. Otherwise nullptr.
63 static std::pair
<AvailabilityResult
, const NamedDecl
*>
64 ShouldDiagnoseAvailabilityOfDecl(Sema
&S
, const NamedDecl
*D
,
66 ObjCInterfaceDecl
*ClassReceiver
) {
67 AvailabilityResult Result
= D
->getAvailability(Message
);
69 // For typedefs, if the typedef declaration appears available look
70 // to the underlying type to see if it is more restrictive.
71 while (const auto *TD
= dyn_cast
<TypedefNameDecl
>(D
)) {
72 if (Result
== AR_Available
) {
73 if (const auto *TT
= TD
->getUnderlyingType()->getAs
<TagType
>()) {
75 Result
= D
->getAvailability(Message
);
82 // Forward class declarations get their attributes from their definition.
83 if (const auto *IDecl
= dyn_cast
<ObjCInterfaceDecl
>(D
)) {
84 if (IDecl
->getDefinition()) {
85 D
= IDecl
->getDefinition();
86 Result
= D
->getAvailability(Message
);
90 if (const auto *ECD
= dyn_cast
<EnumConstantDecl
>(D
))
91 if (Result
== AR_Available
) {
92 const DeclContext
*DC
= ECD
->getDeclContext();
93 if (const auto *TheEnumDecl
= dyn_cast
<EnumDecl
>(DC
)) {
94 Result
= TheEnumDecl
->getAvailability(Message
);
99 // For +new, infer availability from -init.
100 if (const auto *MD
= dyn_cast
<ObjCMethodDecl
>(D
)) {
101 if (S
.NSAPIObj
&& ClassReceiver
) {
102 ObjCMethodDecl
*Init
= ClassReceiver
->lookupInstanceMethod(
103 S
.NSAPIObj
->getInitSelector());
104 if (Init
&& Result
== AR_Available
&& MD
->isClassMethod() &&
105 MD
->getSelector() == S
.NSAPIObj
->getNewSelector() &&
106 MD
->definedInNSObject(S
.getASTContext())) {
107 Result
= Init
->getAvailability(Message
);
117 /// whether we should emit a diagnostic for \c K and \c DeclVersion in
118 /// the context of \c Ctx. For example, we should emit an unavailable diagnostic
119 /// in a deprecated context, but not the other way around.
121 ShouldDiagnoseAvailabilityInContext(Sema
&S
, AvailabilityResult K
,
122 VersionTuple DeclVersion
, Decl
*Ctx
,
123 const NamedDecl
*OffendingDecl
) {
124 assert(K
!= AR_Available
&& "Expected an unavailable declaration here!");
126 // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic.
127 auto DeclLoc
= Ctx
->getBeginLoc();
128 // This is only a problem in Foundation's C++ implementation for CF_OPTIONS.
129 if (DeclLoc
.isMacroID() && S
.getLangOpts().CPlusPlus
&&
130 isa
<TypedefDecl
>(OffendingDecl
)) {
131 StringRef MacroName
= S
.getPreprocessor().getImmediateMacroName(DeclLoc
);
132 if (MacroName
== "CF_OPTIONS" || MacroName
== "OBJC_OPTIONS" ||
133 MacroName
== "SWIFT_OPTIONS" || MacroName
== "NS_OPTIONS") {
138 // Checks if we should emit the availability diagnostic in the context of C.
139 auto CheckContext
= [&](const Decl
*C
) {
140 if (K
== AR_NotYetIntroduced
) {
141 if (const AvailabilityAttr
*AA
= getAttrForPlatform(S
.Context
, C
))
142 if (AA
->getIntroduced() >= DeclVersion
)
144 } else if (K
== AR_Deprecated
) {
145 if (C
->isDeprecated())
147 } else if (K
== AR_Unavailable
) {
148 // It is perfectly fine to refer to an 'unavailable' Objective-C method
149 // when it is referenced from within the @implementation itself. In this
150 // context, we interpret unavailable as a form of access control.
151 if (const auto *MD
= dyn_cast
<ObjCMethodDecl
>(OffendingDecl
)) {
152 if (const auto *Impl
= dyn_cast
<ObjCImplDecl
>(C
)) {
153 if (MD
->getClassInterface() == Impl
->getClassInterface())
159 if (C
->isUnavailable())
165 if (CheckContext(Ctx
))
168 // An implementation implicitly has the availability of the interface.
169 // Unless it is "+load" method.
170 if (const auto *MethodD
= dyn_cast
<ObjCMethodDecl
>(Ctx
))
171 if (MethodD
->isClassMethod() &&
172 MethodD
->getSelector().getAsString() == "load")
175 if (const auto *CatOrImpl
= dyn_cast
<ObjCImplDecl
>(Ctx
)) {
176 if (const ObjCInterfaceDecl
*Interface
= CatOrImpl
->getClassInterface())
177 if (CheckContext(Interface
))
180 // A category implicitly has the availability of the interface.
181 else if (const auto *CatD
= dyn_cast
<ObjCCategoryDecl
>(Ctx
))
182 if (const ObjCInterfaceDecl
*Interface
= CatD
->getClassInterface())
183 if (CheckContext(Interface
))
185 } while ((Ctx
= cast_or_null
<Decl
>(Ctx
->getDeclContext())));
191 shouldDiagnoseAvailabilityByDefault(const ASTContext
&Context
,
192 const VersionTuple
&DeploymentVersion
,
193 const VersionTuple
&DeclVersion
) {
194 const auto &Triple
= Context
.getTargetInfo().getTriple();
195 VersionTuple ForceAvailabilityFromVersion
;
196 switch (Triple
.getOS()) {
197 case llvm::Triple::IOS
:
198 case llvm::Triple::TvOS
:
199 ForceAvailabilityFromVersion
= VersionTuple(/*Major=*/11);
201 case llvm::Triple::WatchOS
:
202 ForceAvailabilityFromVersion
= VersionTuple(/*Major=*/4);
204 case llvm::Triple::Darwin
:
205 case llvm::Triple::MacOSX
:
206 ForceAvailabilityFromVersion
= VersionTuple(/*Major=*/10, /*Minor=*/13);
208 case llvm::Triple::ShaderModel
:
209 // Always enable availability diagnostics for shader models.
212 // New targets should always warn about availability.
213 return Triple
.getVendor() == llvm::Triple::Apple
;
215 return DeploymentVersion
>= ForceAvailabilityFromVersion
||
216 DeclVersion
>= ForceAvailabilityFromVersion
;
219 static NamedDecl
*findEnclosingDeclToAnnotate(Decl
*OrigCtx
) {
220 for (Decl
*Ctx
= OrigCtx
; Ctx
;
221 Ctx
= cast_or_null
<Decl
>(Ctx
->getDeclContext())) {
222 if (isa
<TagDecl
>(Ctx
) || isa
<FunctionDecl
>(Ctx
) || isa
<ObjCMethodDecl
>(Ctx
))
223 return cast
<NamedDecl
>(Ctx
);
224 if (auto *CD
= dyn_cast
<ObjCContainerDecl
>(Ctx
)) {
225 if (auto *Imp
= dyn_cast
<ObjCImplDecl
>(Ctx
))
226 return Imp
->getClassInterface();
231 return dyn_cast
<NamedDecl
>(OrigCtx
);
236 struct AttributeInsertion
{
241 static AttributeInsertion
createInsertionAfter(const NamedDecl
*D
) {
242 return {" ", D
->getEndLoc(), ""};
244 static AttributeInsertion
createInsertionAfter(SourceLocation Loc
) {
245 return {" ", Loc
, ""};
247 static AttributeInsertion
createInsertionBefore(const NamedDecl
*D
) {
248 return {"", D
->getBeginLoc(), "\n"};
252 } // end anonymous namespace
254 /// Tries to parse a string as ObjC method name.
256 /// \param Name The string to parse. Expected to originate from availability
257 /// attribute argument.
258 /// \param SlotNames The vector that will be populated with slot names. In case
259 /// of unsuccessful parsing can contain invalid data.
260 /// \returns A number of method parameters if parsing was successful,
261 /// std::nullopt otherwise.
262 static std::optional
<unsigned>
263 tryParseObjCMethodName(StringRef Name
, SmallVectorImpl
<StringRef
> &SlotNames
,
264 const LangOptions
&LangOpts
) {
265 // Accept replacements starting with - or + as valid ObjC method names.
266 if (!Name
.empty() && (Name
.front() == '-' || Name
.front() == '+'))
267 Name
= Name
.drop_front(1);
270 Name
.split(SlotNames
, ':');
272 if (Name
.back() == ':') {
273 // Remove an empty string at the end that doesn't represent any slot.
274 SlotNames
.pop_back();
275 NumParams
= SlotNames
.size();
277 if (SlotNames
.size() != 1)
278 // Not a valid method name, just a colon-separated string.
282 // Verify all slot names are valid.
283 bool AllowDollar
= LangOpts
.DollarIdents
;
284 for (StringRef S
: SlotNames
) {
287 if (!isValidAsciiIdentifier(S
, AllowDollar
))
293 /// Returns a source location in which it's appropriate to insert a new
294 /// attribute for the given declaration \D.
295 static std::optional
<AttributeInsertion
>
296 createAttributeInsertion(const NamedDecl
*D
, const SourceManager
&SM
,
297 const LangOptions
&LangOpts
) {
298 if (isa
<ObjCPropertyDecl
>(D
))
299 return AttributeInsertion::createInsertionAfter(D
);
300 if (const auto *MD
= dyn_cast
<ObjCMethodDecl
>(D
)) {
303 return AttributeInsertion::createInsertionAfter(D
);
305 if (const auto *TD
= dyn_cast
<TagDecl
>(D
)) {
307 Lexer::getLocForEndOfToken(TD
->getInnerLocStart(), 0, SM
, LangOpts
);
310 // Insert after the 'struct'/whatever keyword.
311 return AttributeInsertion::createInsertionAfter(Loc
);
313 return AttributeInsertion::createInsertionBefore(D
);
316 /// Actually emit an availability diagnostic for a reference to an unavailable
319 /// \param Ctx The context that the reference occurred in
320 /// \param ReferringDecl The exact declaration that was referenced.
321 /// \param OffendingDecl A related decl to \c ReferringDecl that has an
322 /// availability attribute corresponding to \c K attached to it. Note that this
323 /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and
324 /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl
325 /// and OffendingDecl is the EnumDecl.
326 static void DoEmitAvailabilityWarning(Sema
&S
, AvailabilityResult K
,
327 Decl
*Ctx
, const NamedDecl
*ReferringDecl
,
328 const NamedDecl
*OffendingDecl
,
330 ArrayRef
<SourceLocation
> Locs
,
331 const ObjCInterfaceDecl
*UnknownObjCClass
,
332 const ObjCPropertyDecl
*ObjCProperty
,
333 bool ObjCPropertyAccess
) {
334 // Diagnostics for deprecated or unavailable.
335 unsigned diag
, diag_message
, diag_fwdclass_message
;
336 unsigned diag_available_here
= diag::note_availability_specified_here
;
337 SourceLocation NoteLocation
= OffendingDecl
->getLocation();
339 // Matches 'diag::note_property_attribute' options.
340 unsigned property_note_select
;
342 // Matches diag::note_availability_specified_here.
343 unsigned available_here_select_kind
;
345 VersionTuple DeclVersion
;
346 if (const AvailabilityAttr
*AA
= getAttrForPlatform(S
.Context
, OffendingDecl
))
347 DeclVersion
= AA
->getIntroduced();
349 if (!ShouldDiagnoseAvailabilityInContext(S
, K
, DeclVersion
, Ctx
,
353 SourceLocation Loc
= Locs
.front();
355 // The declaration can have multiple availability attributes, we are looking
357 const AvailabilityAttr
*A
= getAttrForPlatform(S
.Context
, OffendingDecl
);
358 if (A
&& A
->isInherited()) {
359 for (const Decl
*Redecl
= OffendingDecl
->getMostRecentDecl(); Redecl
;
360 Redecl
= Redecl
->getPreviousDecl()) {
361 const AvailabilityAttr
*AForRedecl
=
362 getAttrForPlatform(S
.Context
, Redecl
);
363 if (AForRedecl
&& !AForRedecl
->isInherited()) {
364 // If D is a declaration with inherited attributes, the note should
365 // point to the declaration with actual attributes.
366 NoteLocation
= Redecl
->getLocation();
373 case AR_NotYetIntroduced
: {
374 // We would like to emit the diagnostic even if -Wunguarded-availability is
375 // not specified for deployment targets >= to iOS 11 or equivalent or
376 // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
378 const AvailabilityAttr
*AA
=
379 getAttrForPlatform(S
.getASTContext(), OffendingDecl
);
380 VersionTuple Introduced
= AA
->getIntroduced();
382 bool UseNewWarning
= shouldDiagnoseAvailabilityByDefault(
383 S
.Context
, S
.Context
.getTargetInfo().getPlatformMinVersion(),
385 unsigned Warning
= UseNewWarning
? diag::warn_unguarded_availability_new
386 : diag::warn_unguarded_availability
;
388 std::string
PlatformName(AvailabilityAttr::getPrettyPlatformName(
389 S
.getASTContext().getTargetInfo().getPlatformName()));
391 S
.Diag(Loc
, Warning
) << OffendingDecl
<< PlatformName
392 << Introduced
.getAsString();
394 S
.Diag(OffendingDecl
->getLocation(),
395 diag::note_partial_availability_specified_here
)
396 << OffendingDecl
<< PlatformName
<< Introduced
.getAsString()
397 << S
.Context
.getTargetInfo().getPlatformMinVersion().getAsString();
399 if (const auto *Enclosing
= findEnclosingDeclToAnnotate(Ctx
)) {
400 if (const auto *TD
= dyn_cast
<TagDecl
>(Enclosing
))
401 if (TD
->getDeclName().isEmpty()) {
402 S
.Diag(TD
->getLocation(),
403 diag::note_decl_unguarded_availability_silence
)
404 << /*Anonymous*/ 1 << TD
->getKindName();
408 S
.Diag(Enclosing
->getLocation(),
409 diag::note_decl_unguarded_availability_silence
)
410 << /*Named*/ 0 << Enclosing
;
411 // Don't offer a fixit for declarations with availability attributes.
412 if (Enclosing
->hasAttr
<AvailabilityAttr
>())
414 if (!S
.getPreprocessor().isMacroDefined("API_AVAILABLE"))
416 std::optional
<AttributeInsertion
> Insertion
= createAttributeInsertion(
417 Enclosing
, S
.getSourceManager(), S
.getLangOpts());
420 std::string PlatformName
=
421 AvailabilityAttr::getPlatformNameSourceSpelling(
422 S
.getASTContext().getTargetInfo().getPlatformName())
424 std::string Introduced
=
425 OffendingDecl
->getVersionIntroduced().getAsString();
426 FixitNoteDiag
<< FixItHint::CreateInsertion(
428 (llvm::Twine(Insertion
->Prefix
) + "API_AVAILABLE(" + PlatformName
+
429 "(" + Introduced
+ "))" + Insertion
->Suffix
)
435 diag
= !ObjCPropertyAccess
? diag::warn_deprecated
436 : diag::warn_property_method_deprecated
;
437 diag_message
= diag::warn_deprecated_message
;
438 diag_fwdclass_message
= diag::warn_deprecated_fwdclass_message
;
439 property_note_select
= /* deprecated */ 0;
440 available_here_select_kind
= /* deprecated */ 2;
441 if (const auto *AL
= OffendingDecl
->getAttr
<DeprecatedAttr
>())
442 NoteLocation
= AL
->getLocation();
446 diag
= !ObjCPropertyAccess
? diag::err_unavailable
447 : diag::err_property_method_unavailable
;
448 diag_message
= diag::err_unavailable_message
;
449 diag_fwdclass_message
= diag::warn_unavailable_fwdclass_message
;
450 property_note_select
= /* unavailable */ 1;
451 available_here_select_kind
= /* unavailable */ 0;
453 if (auto AL
= OffendingDecl
->getAttr
<UnavailableAttr
>()) {
454 if (AL
->isImplicit() && AL
->getImplicitReason()) {
455 // Most of these failures are due to extra restrictions in ARC;
456 // reflect that in the primary diagnostic when applicable.
457 auto flagARCError
= [&] {
458 if (S
.getLangOpts().ObjCAutoRefCount
&&
459 S
.getSourceManager().isInSystemHeader(
460 OffendingDecl
->getLocation()))
461 diag
= diag::err_unavailable_in_arc
;
464 switch (AL
->getImplicitReason()) {
465 case UnavailableAttr::IR_None
: break;
467 case UnavailableAttr::IR_ARCForbiddenType
:
469 diag_available_here
= diag::note_arc_forbidden_type
;
472 case UnavailableAttr::IR_ForbiddenWeak
:
473 if (S
.getLangOpts().ObjCWeakRuntime
)
474 diag_available_here
= diag::note_arc_weak_disabled
;
476 diag_available_here
= diag::note_arc_weak_no_runtime
;
479 case UnavailableAttr::IR_ARCForbiddenConversion
:
481 diag_available_here
= diag::note_performs_forbidden_arc_conversion
;
484 case UnavailableAttr::IR_ARCInitReturnsUnrelated
:
486 diag_available_here
= diag::note_arc_init_returns_unrelated
;
489 case UnavailableAttr::IR_ARCFieldWithOwnership
:
491 diag_available_here
= diag::note_arc_field_with_ownership
;
499 llvm_unreachable("Warning for availability of available declaration?");
502 SmallVector
<FixItHint
, 12> FixIts
;
503 if (K
== AR_Deprecated
) {
504 StringRef Replacement
;
505 if (auto AL
= OffendingDecl
->getAttr
<DeprecatedAttr
>())
506 Replacement
= AL
->getReplacement();
507 if (auto AL
= getAttrForPlatform(S
.Context
, OffendingDecl
))
508 Replacement
= AL
->getReplacement();
510 CharSourceRange UseRange
;
511 if (!Replacement
.empty())
513 CharSourceRange::getCharRange(Loc
, S
.getLocForEndOfToken(Loc
));
514 if (UseRange
.isValid()) {
515 if (const auto *MethodDecl
= dyn_cast
<ObjCMethodDecl
>(ReferringDecl
)) {
516 Selector Sel
= MethodDecl
->getSelector();
517 SmallVector
<StringRef
, 12> SelectorSlotNames
;
518 std::optional
<unsigned> NumParams
= tryParseObjCMethodName(
519 Replacement
, SelectorSlotNames
, S
.getLangOpts());
520 if (NumParams
&& *NumParams
== Sel
.getNumArgs()) {
521 assert(SelectorSlotNames
.size() == Locs
.size());
522 for (unsigned I
= 0; I
< Locs
.size(); ++I
) {
523 if (!Sel
.getNameForSlot(I
).empty()) {
524 CharSourceRange NameRange
= CharSourceRange::getCharRange(
525 Locs
[I
], S
.getLocForEndOfToken(Locs
[I
]));
526 FixIts
.push_back(FixItHint::CreateReplacement(
527 NameRange
, SelectorSlotNames
[I
]));
530 FixItHint::CreateInsertion(Locs
[I
], SelectorSlotNames
[I
]));
533 FixIts
.push_back(FixItHint::CreateReplacement(UseRange
, Replacement
));
535 FixIts
.push_back(FixItHint::CreateReplacement(UseRange
, Replacement
));
539 if (!Message
.empty()) {
540 S
.Diag(Loc
, diag_message
) << ReferringDecl
<< Message
<< FixIts
;
542 S
.Diag(ObjCProperty
->getLocation(), diag::note_property_attribute
)
543 << ObjCProperty
->getDeclName() << property_note_select
;
544 } else if (!UnknownObjCClass
) {
545 S
.Diag(Loc
, diag
) << ReferringDecl
<< FixIts
;
547 S
.Diag(ObjCProperty
->getLocation(), diag::note_property_attribute
)
548 << ObjCProperty
->getDeclName() << property_note_select
;
550 S
.Diag(Loc
, diag_fwdclass_message
) << ReferringDecl
<< FixIts
;
551 S
.Diag(UnknownObjCClass
->getLocation(), diag::note_forward_class
);
554 S
.Diag(NoteLocation
, diag_available_here
)
555 << OffendingDecl
<< available_here_select_kind
;
558 void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic
&DD
, Decl
*Ctx
) {
559 assert(DD
.Kind
== DelayedDiagnostic::Availability
&&
560 "Expected an availability diagnostic here");
563 DoEmitAvailabilityWarning(
564 *this, DD
.getAvailabilityResult(), Ctx
, DD
.getAvailabilityReferringDecl(),
565 DD
.getAvailabilityOffendingDecl(), DD
.getAvailabilityMessage(),
566 DD
.getAvailabilitySelectorLocs(), DD
.getUnknownObjCClass(),
567 DD
.getObjCProperty(), false);
570 static void EmitAvailabilityWarning(Sema
&S
, AvailabilityResult AR
,
571 const NamedDecl
*ReferringDecl
,
572 const NamedDecl
*OffendingDecl
,
574 ArrayRef
<SourceLocation
> Locs
,
575 const ObjCInterfaceDecl
*UnknownObjCClass
,
576 const ObjCPropertyDecl
*ObjCProperty
,
577 bool ObjCPropertyAccess
) {
578 // Delay if we're currently parsing a declaration.
579 if (S
.DelayedDiagnostics
.shouldDelayDiagnostics()) {
580 S
.DelayedDiagnostics
.add(
581 DelayedDiagnostic::makeAvailability(
582 AR
, Locs
, ReferringDecl
, OffendingDecl
, UnknownObjCClass
,
583 ObjCProperty
, Message
, ObjCPropertyAccess
));
587 Decl
*Ctx
= cast
<Decl
>(S
.getCurLexicalContext());
588 DoEmitAvailabilityWarning(S
, AR
, Ctx
, ReferringDecl
, OffendingDecl
,
589 Message
, Locs
, UnknownObjCClass
, ObjCProperty
,
595 /// Returns true if the given statement can be a body-like child of \p Parent.
596 bool isBodyLikeChildStmt(const Stmt
*S
, const Stmt
*Parent
) {
597 switch (Parent
->getStmtClass()) {
598 case Stmt::IfStmtClass
:
599 return cast
<IfStmt
>(Parent
)->getThen() == S
||
600 cast
<IfStmt
>(Parent
)->getElse() == S
;
601 case Stmt::WhileStmtClass
:
602 return cast
<WhileStmt
>(Parent
)->getBody() == S
;
603 case Stmt::DoStmtClass
:
604 return cast
<DoStmt
>(Parent
)->getBody() == S
;
605 case Stmt::ForStmtClass
:
606 return cast
<ForStmt
>(Parent
)->getBody() == S
;
607 case Stmt::CXXForRangeStmtClass
:
608 return cast
<CXXForRangeStmt
>(Parent
)->getBody() == S
;
609 case Stmt::ObjCForCollectionStmtClass
:
610 return cast
<ObjCForCollectionStmt
>(Parent
)->getBody() == S
;
611 case Stmt::CaseStmtClass
:
612 case Stmt::DefaultStmtClass
:
613 return cast
<SwitchCase
>(Parent
)->getSubStmt() == S
;
619 class StmtUSEFinder
: public RecursiveASTVisitor
<StmtUSEFinder
> {
623 bool VisitStmt(Stmt
*S
) { return S
!= Target
; }
625 /// Returns true if the given statement is present in the given declaration.
626 static bool isContained(const Stmt
*Target
, const Decl
*D
) {
627 StmtUSEFinder Visitor
;
628 Visitor
.Target
= Target
;
629 return !Visitor
.TraverseDecl(const_cast<Decl
*>(D
));
633 /// Traverses the AST and finds the last statement that used a given
635 class LastDeclUSEFinder
: public RecursiveASTVisitor
<LastDeclUSEFinder
> {
639 bool VisitDeclRefExpr(DeclRefExpr
*DRE
) {
640 if (DRE
->getDecl() == D
)
645 static const Stmt
*findLastStmtThatUsesDecl(const Decl
*D
,
646 const CompoundStmt
*Scope
) {
647 LastDeclUSEFinder Visitor
;
649 for (const Stmt
*S
: llvm::reverse(Scope
->body())) {
650 if (!Visitor
.TraverseStmt(const_cast<Stmt
*>(S
)))
657 /// This class implements -Wunguarded-availability.
659 /// This is done with a traversal of the AST of a function that makes reference
660 /// to a partially available declaration. Whenever we encounter an \c if of the
661 /// form: \c if(@available(...)), we use the version from the condition to visit
662 /// the then statement.
663 class DiagnoseUnguardedAvailability
664 : public RecursiveASTVisitor
<DiagnoseUnguardedAvailability
> {
665 typedef RecursiveASTVisitor
<DiagnoseUnguardedAvailability
> Base
;
670 /// Stack of potentially nested 'if (@available(...))'s.
671 SmallVector
<VersionTuple
, 8> AvailabilityStack
;
672 SmallVector
<const Stmt
*, 16> StmtStack
;
674 void DiagnoseDeclAvailability(NamedDecl
*D
, SourceRange Range
,
675 ObjCInterfaceDecl
*ClassReceiver
= nullptr);
678 DiagnoseUnguardedAvailability(Sema
&SemaRef
, Decl
*Ctx
)
679 : SemaRef(SemaRef
), Ctx(Ctx
) {
680 AvailabilityStack
.push_back(
681 SemaRef
.Context
.getTargetInfo().getPlatformMinVersion());
684 bool TraverseStmt(Stmt
*S
) {
687 StmtStack
.push_back(S
);
688 bool Result
= Base::TraverseStmt(S
);
689 StmtStack
.pop_back();
693 void IssueDiagnostics(Stmt
*S
) { TraverseStmt(S
); }
695 bool TraverseIfStmt(IfStmt
*If
);
697 // for 'case X:' statements, don't bother looking at the 'X'; it can't lead
698 // to any useful diagnostics.
699 bool TraverseCaseStmt(CaseStmt
*CS
) { return TraverseStmt(CS
->getSubStmt()); }
701 bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr
*PRE
) { return true; }
703 bool VisitObjCMessageExpr(ObjCMessageExpr
*Msg
) {
704 if (ObjCMethodDecl
*D
= Msg
->getMethodDecl()) {
705 ObjCInterfaceDecl
*ID
= nullptr;
706 QualType ReceiverTy
= Msg
->getClassReceiver();
707 if (!ReceiverTy
.isNull() && ReceiverTy
->getAsObjCInterfaceType())
708 ID
= ReceiverTy
->getAsObjCInterfaceType()->getInterface();
710 DiagnoseDeclAvailability(
711 D
, SourceRange(Msg
->getSelectorStartLoc(), Msg
->getEndLoc()), ID
);
716 bool VisitDeclRefExpr(DeclRefExpr
*DRE
) {
717 DiagnoseDeclAvailability(DRE
->getDecl(),
718 SourceRange(DRE
->getBeginLoc(), DRE
->getEndLoc()));
722 bool VisitMemberExpr(MemberExpr
*ME
) {
723 DiagnoseDeclAvailability(ME
->getMemberDecl(),
724 SourceRange(ME
->getBeginLoc(), ME
->getEndLoc()));
728 bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr
*E
) {
729 SemaRef
.Diag(E
->getBeginLoc(), diag::warn_at_available_unchecked_use
)
730 << (!SemaRef
.getLangOpts().ObjC
);
734 bool VisitTypeLoc(TypeLoc Ty
);
737 void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
738 NamedDecl
*D
, SourceRange Range
, ObjCInterfaceDecl
*ReceiverClass
) {
739 AvailabilityResult Result
;
740 const NamedDecl
*OffendingDecl
;
741 std::tie(Result
, OffendingDecl
) =
742 ShouldDiagnoseAvailabilityOfDecl(SemaRef
, D
, nullptr, ReceiverClass
);
743 if (Result
!= AR_Available
) {
744 // All other diagnostic kinds have already been handled in
745 // DiagnoseAvailabilityOfDecl.
746 if (Result
!= AR_NotYetIntroduced
)
749 const AvailabilityAttr
*AA
=
750 getAttrForPlatform(SemaRef
.getASTContext(), OffendingDecl
);
751 VersionTuple Introduced
= AA
->getIntroduced();
753 if (AvailabilityStack
.back() >= Introduced
)
756 // If the context of this function is less available than D, we should not
757 // emit a diagnostic.
758 if (!ShouldDiagnoseAvailabilityInContext(SemaRef
, Result
, Introduced
, Ctx
,
762 // We would like to emit the diagnostic even if -Wunguarded-availability is
763 // not specified for deployment targets >= to iOS 11 or equivalent or
764 // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
767 shouldDiagnoseAvailabilityByDefault(
769 SemaRef
.Context
.getTargetInfo().getPlatformMinVersion(), Introduced
)
770 ? diag::warn_unguarded_availability_new
771 : diag::warn_unguarded_availability
;
773 std::string
PlatformName(AvailabilityAttr::getPrettyPlatformName(
774 SemaRef
.getASTContext().getTargetInfo().getPlatformName()));
776 SemaRef
.Diag(Range
.getBegin(), DiagKind
)
777 << Range
<< D
<< PlatformName
<< Introduced
.getAsString();
779 SemaRef
.Diag(OffendingDecl
->getLocation(),
780 diag::note_partial_availability_specified_here
)
781 << OffendingDecl
<< PlatformName
<< Introduced
.getAsString()
782 << SemaRef
.Context
.getTargetInfo()
783 .getPlatformMinVersion()
787 SemaRef
.Diag(Range
.getBegin(), diag::note_unguarded_available_silence
)
789 << (SemaRef
.getLangOpts().ObjC
? /*@available*/ 0
790 : /*__builtin_available*/ 1);
792 // Find the statement which should be enclosed in the if @available check.
793 if (StmtStack
.empty())
795 const Stmt
*StmtOfUse
= StmtStack
.back();
796 const CompoundStmt
*Scope
= nullptr;
797 for (const Stmt
*S
: llvm::reverse(StmtStack
)) {
798 if (const auto *CS
= dyn_cast
<CompoundStmt
>(S
)) {
802 if (isBodyLikeChildStmt(StmtOfUse
, S
)) {
803 // The declaration won't be seen outside of the statement, so we don't
804 // have to wrap the uses of any declared variables in if (@available).
805 // Therefore we can avoid setting Scope here.
810 const Stmt
*LastStmtOfUse
= nullptr;
811 if (isa
<DeclStmt
>(StmtOfUse
) && Scope
) {
812 for (const Decl
*D
: cast
<DeclStmt
>(StmtOfUse
)->decls()) {
813 if (StmtUSEFinder::isContained(StmtStack
.back(), D
)) {
814 LastStmtOfUse
= LastDeclUSEFinder::findLastStmtThatUsesDecl(D
, Scope
);
820 const SourceManager
&SM
= SemaRef
.getSourceManager();
821 SourceLocation IfInsertionLoc
=
822 SM
.getExpansionLoc(StmtOfUse
->getBeginLoc());
823 SourceLocation StmtEndLoc
=
824 SM
.getExpansionRange(
825 (LastStmtOfUse
? LastStmtOfUse
: StmtOfUse
)->getEndLoc())
827 if (SM
.getFileID(IfInsertionLoc
) != SM
.getFileID(StmtEndLoc
))
830 StringRef Indentation
= Lexer::getIndentationForLine(IfInsertionLoc
, SM
);
831 const char *ExtraIndentation
= " ";
832 std::string FixItString
;
833 llvm::raw_string_ostream
FixItOS(FixItString
);
834 FixItOS
<< "if (" << (SemaRef
.getLangOpts().ObjC
? "@available"
835 : "__builtin_available")
837 << AvailabilityAttr::getPlatformNameSourceSpelling(
838 SemaRef
.getASTContext().getTargetInfo().getPlatformName())
839 << " " << Introduced
.getAsString() << ", *)) {\n"
840 << Indentation
<< ExtraIndentation
;
841 FixitDiag
<< FixItHint::CreateInsertion(IfInsertionLoc
, FixItOS
.str());
842 SourceLocation ElseInsertionLoc
= Lexer::findLocationAfterToken(
843 StmtEndLoc
, tok::semi
, SM
, SemaRef
.getLangOpts(),
844 /*SkipTrailingWhitespaceAndNewLine=*/false);
845 if (ElseInsertionLoc
.isInvalid())
847 Lexer::getLocForEndOfToken(StmtEndLoc
, 0, SM
, SemaRef
.getLangOpts());
848 FixItOS
.str().clear();
850 << Indentation
<< "} else {\n"
851 << Indentation
<< ExtraIndentation
852 << "// Fallback on earlier versions\n"
853 << Indentation
<< "}";
854 FixitDiag
<< FixItHint::CreateInsertion(ElseInsertionLoc
, FixItOS
.str());
858 bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty
) {
859 const Type
*TyPtr
= Ty
.getTypePtr();
860 SourceRange Range
{Ty
.getBeginLoc(), Ty
.getEndLoc()};
862 if (Range
.isInvalid())
865 if (const auto *TT
= dyn_cast
<TagType
>(TyPtr
)) {
866 TagDecl
*TD
= TT
->getDecl();
867 DiagnoseDeclAvailability(TD
, Range
);
869 } else if (const auto *TD
= dyn_cast
<TypedefType
>(TyPtr
)) {
870 TypedefNameDecl
*D
= TD
->getDecl();
871 DiagnoseDeclAvailability(D
, Range
);
873 } else if (const auto *ObjCO
= dyn_cast
<ObjCObjectType
>(TyPtr
)) {
874 if (NamedDecl
*D
= ObjCO
->getInterface())
875 DiagnoseDeclAvailability(D
, Range
);
881 bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt
*If
) {
882 VersionTuple CondVersion
;
883 if (auto *E
= dyn_cast
<ObjCAvailabilityCheckExpr
>(If
->getCond())) {
884 CondVersion
= E
->getVersion();
886 // If we're using the '*' case here or if this check is redundant, then we
887 // use the enclosing version to check both branches.
888 if (CondVersion
.empty() || CondVersion
<= AvailabilityStack
.back())
889 return TraverseStmt(If
->getThen()) && TraverseStmt(If
->getElse());
891 // This isn't an availability checking 'if', we can just continue.
892 return Base::TraverseIfStmt(If
);
895 AvailabilityStack
.push_back(CondVersion
);
896 bool ShouldContinue
= TraverseStmt(If
->getThen());
897 AvailabilityStack
.pop_back();
899 return ShouldContinue
&& TraverseStmt(If
->getElse());
902 } // end anonymous namespace
904 void Sema::DiagnoseUnguardedAvailabilityViolations(Decl
*D
) {
905 Stmt
*Body
= nullptr;
907 if (auto *FD
= D
->getAsFunction()) {
908 // FIXME: We only examine the pattern decl for availability violations now,
909 // but we should also examine instantiated templates.
910 if (FD
->isTemplateInstantiation())
913 Body
= FD
->getBody();
915 if (auto *CD
= dyn_cast
<CXXConstructorDecl
>(FD
))
916 for (const CXXCtorInitializer
*CI
: CD
->inits())
917 DiagnoseUnguardedAvailability(*this, D
).IssueDiagnostics(CI
->getInit());
919 } else if (auto *MD
= dyn_cast
<ObjCMethodDecl
>(D
))
920 Body
= MD
->getBody();
921 else if (auto *BD
= dyn_cast
<BlockDecl
>(D
))
922 Body
= BD
->getBody();
924 assert(Body
&& "Need a body here!");
926 DiagnoseUnguardedAvailability(*this, D
).IssueDiagnostics(Body
);
929 FunctionScopeInfo
*Sema::getCurFunctionAvailabilityContext() {
930 if (FunctionScopes
.empty())
933 // Conservatively search the entire current function scope context for
934 // availability violations. This ensures we always correctly analyze nested
935 // classes, blocks, lambdas, etc. that may or may not be inside if(@available)
936 // checks themselves.
937 return FunctionScopes
.front();
940 void Sema::DiagnoseAvailabilityOfDecl(NamedDecl
*D
,
941 ArrayRef
<SourceLocation
> Locs
,
942 const ObjCInterfaceDecl
*UnknownObjCClass
,
943 bool ObjCPropertyAccess
,
944 bool AvoidPartialAvailabilityChecks
,
945 ObjCInterfaceDecl
*ClassReceiver
) {
947 AvailabilityResult Result
;
948 const NamedDecl
* OffendingDecl
;
949 // See if this declaration is unavailable, deprecated, or partial.
950 std::tie(Result
, OffendingDecl
) =
951 ShouldDiagnoseAvailabilityOfDecl(*this, D
, &Message
, ClassReceiver
);
952 if (Result
== AR_Available
)
955 if (Result
== AR_NotYetIntroduced
) {
956 if (AvoidPartialAvailabilityChecks
)
959 // We need to know the @available context in the current function to
960 // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that
961 // when we're done parsing the current function.
962 if (FunctionScopeInfo
*Context
= getCurFunctionAvailabilityContext()) {
963 Context
->HasPotentialAvailabilityViolations
= true;
968 const ObjCPropertyDecl
*ObjCPDecl
= nullptr;
969 if (const auto *MD
= dyn_cast
<ObjCMethodDecl
>(D
)) {
970 if (const ObjCPropertyDecl
*PD
= MD
->findPropertyDecl()) {
971 AvailabilityResult PDeclResult
= PD
->getAvailability(nullptr);
972 if (PDeclResult
== Result
)
977 EmitAvailabilityWarning(*this, Result
, D
, OffendingDecl
, Message
, Locs
,
978 UnknownObjCClass
, ObjCPDecl
, ObjCPropertyAccess
);