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 // Checks if we should emit the availability diagnostic in the context of C.
127 auto CheckContext
= [&](const Decl
*C
) {
128 if (K
== AR_NotYetIntroduced
) {
129 if (const AvailabilityAttr
*AA
= getAttrForPlatform(S
.Context
, C
))
130 if (AA
->getIntroduced() >= DeclVersion
)
132 } else if (K
== AR_Deprecated
) {
133 if (C
->isDeprecated())
135 } else if (K
== AR_Unavailable
) {
136 // It is perfectly fine to refer to an 'unavailable' Objective-C method
137 // when it is referenced from within the @implementation itself. In this
138 // context, we interpret unavailable as a form of access control.
139 if (const auto *MD
= dyn_cast
<ObjCMethodDecl
>(OffendingDecl
)) {
140 if (const auto *Impl
= dyn_cast
<ObjCImplDecl
>(C
)) {
141 if (MD
->getClassInterface() == Impl
->getClassInterface())
147 if (C
->isUnavailable())
153 if (CheckContext(Ctx
))
156 // An implementation implicitly has the availability of the interface.
157 // Unless it is "+load" method.
158 if (const auto *MethodD
= dyn_cast
<ObjCMethodDecl
>(Ctx
))
159 if (MethodD
->isClassMethod() &&
160 MethodD
->getSelector().getAsString() == "load")
163 if (const auto *CatOrImpl
= dyn_cast
<ObjCImplDecl
>(Ctx
)) {
164 if (const ObjCInterfaceDecl
*Interface
= CatOrImpl
->getClassInterface())
165 if (CheckContext(Interface
))
168 // A category implicitly has the availability of the interface.
169 else if (const auto *CatD
= dyn_cast
<ObjCCategoryDecl
>(Ctx
))
170 if (const ObjCInterfaceDecl
*Interface
= CatD
->getClassInterface())
171 if (CheckContext(Interface
))
173 } while ((Ctx
= cast_or_null
<Decl
>(Ctx
->getDeclContext())));
179 shouldDiagnoseAvailabilityByDefault(const ASTContext
&Context
,
180 const VersionTuple
&DeploymentVersion
,
181 const VersionTuple
&DeclVersion
) {
182 const auto &Triple
= Context
.getTargetInfo().getTriple();
183 VersionTuple ForceAvailabilityFromVersion
;
184 switch (Triple
.getOS()) {
185 case llvm::Triple::IOS
:
186 case llvm::Triple::TvOS
:
187 ForceAvailabilityFromVersion
= VersionTuple(/*Major=*/11);
189 case llvm::Triple::WatchOS
:
190 ForceAvailabilityFromVersion
= VersionTuple(/*Major=*/4);
192 case llvm::Triple::Darwin
:
193 case llvm::Triple::MacOSX
:
194 ForceAvailabilityFromVersion
= VersionTuple(/*Major=*/10, /*Minor=*/13);
196 case llvm::Triple::ShaderModel
:
197 // Always enable availability diagnostics for shader models.
200 // New targets should always warn about availability.
201 return Triple
.getVendor() == llvm::Triple::Apple
;
203 return DeploymentVersion
>= ForceAvailabilityFromVersion
||
204 DeclVersion
>= ForceAvailabilityFromVersion
;
207 static NamedDecl
*findEnclosingDeclToAnnotate(Decl
*OrigCtx
) {
208 for (Decl
*Ctx
= OrigCtx
; Ctx
;
209 Ctx
= cast_or_null
<Decl
>(Ctx
->getDeclContext())) {
210 if (isa
<TagDecl
>(Ctx
) || isa
<FunctionDecl
>(Ctx
) || isa
<ObjCMethodDecl
>(Ctx
))
211 return cast
<NamedDecl
>(Ctx
);
212 if (auto *CD
= dyn_cast
<ObjCContainerDecl
>(Ctx
)) {
213 if (auto *Imp
= dyn_cast
<ObjCImplDecl
>(Ctx
))
214 return Imp
->getClassInterface();
219 return dyn_cast
<NamedDecl
>(OrigCtx
);
224 struct AttributeInsertion
{
229 static AttributeInsertion
createInsertionAfter(const NamedDecl
*D
) {
230 return {" ", D
->getEndLoc(), ""};
232 static AttributeInsertion
createInsertionAfter(SourceLocation Loc
) {
233 return {" ", Loc
, ""};
235 static AttributeInsertion
createInsertionBefore(const NamedDecl
*D
) {
236 return {"", D
->getBeginLoc(), "\n"};
240 } // end anonymous namespace
242 /// Tries to parse a string as ObjC method name.
244 /// \param Name The string to parse. Expected to originate from availability
245 /// attribute argument.
246 /// \param SlotNames The vector that will be populated with slot names. In case
247 /// of unsuccessful parsing can contain invalid data.
248 /// \returns A number of method parameters if parsing was successful,
249 /// std::nullopt otherwise.
250 static std::optional
<unsigned>
251 tryParseObjCMethodName(StringRef Name
, SmallVectorImpl
<StringRef
> &SlotNames
,
252 const LangOptions
&LangOpts
) {
253 // Accept replacements starting with - or + as valid ObjC method names.
254 if (!Name
.empty() && (Name
.front() == '-' || Name
.front() == '+'))
255 Name
= Name
.drop_front(1);
258 Name
.split(SlotNames
, ':');
260 if (Name
.back() == ':') {
261 // Remove an empty string at the end that doesn't represent any slot.
262 SlotNames
.pop_back();
263 NumParams
= SlotNames
.size();
265 if (SlotNames
.size() != 1)
266 // Not a valid method name, just a colon-separated string.
270 // Verify all slot names are valid.
271 bool AllowDollar
= LangOpts
.DollarIdents
;
272 for (StringRef S
: SlotNames
) {
275 if (!isValidAsciiIdentifier(S
, AllowDollar
))
281 /// Returns a source location in which it's appropriate to insert a new
282 /// attribute for the given declaration \D.
283 static std::optional
<AttributeInsertion
>
284 createAttributeInsertion(const NamedDecl
*D
, const SourceManager
&SM
,
285 const LangOptions
&LangOpts
) {
286 if (isa
<ObjCPropertyDecl
>(D
))
287 return AttributeInsertion::createInsertionAfter(D
);
288 if (const auto *MD
= dyn_cast
<ObjCMethodDecl
>(D
)) {
291 return AttributeInsertion::createInsertionAfter(D
);
293 if (const auto *TD
= dyn_cast
<TagDecl
>(D
)) {
295 Lexer::getLocForEndOfToken(TD
->getInnerLocStart(), 0, SM
, LangOpts
);
298 // Insert after the 'struct'/whatever keyword.
299 return AttributeInsertion::createInsertionAfter(Loc
);
301 return AttributeInsertion::createInsertionBefore(D
);
304 /// Actually emit an availability diagnostic for a reference to an unavailable
307 /// \param Ctx The context that the reference occurred in
308 /// \param ReferringDecl The exact declaration that was referenced.
309 /// \param OffendingDecl A related decl to \c ReferringDecl that has an
310 /// availability attribute corresponding to \c K attached to it. Note that this
311 /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and
312 /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl
313 /// and OffendingDecl is the EnumDecl.
314 static void DoEmitAvailabilityWarning(Sema
&S
, AvailabilityResult K
,
315 Decl
*Ctx
, const NamedDecl
*ReferringDecl
,
316 const NamedDecl
*OffendingDecl
,
318 ArrayRef
<SourceLocation
> Locs
,
319 const ObjCInterfaceDecl
*UnknownObjCClass
,
320 const ObjCPropertyDecl
*ObjCProperty
,
321 bool ObjCPropertyAccess
) {
322 // Diagnostics for deprecated or unavailable.
323 unsigned diag
, diag_message
, diag_fwdclass_message
;
324 unsigned diag_available_here
= diag::note_availability_specified_here
;
325 SourceLocation NoteLocation
= OffendingDecl
->getLocation();
327 // Matches 'diag::note_property_attribute' options.
328 unsigned property_note_select
;
330 // Matches diag::note_availability_specified_here.
331 unsigned available_here_select_kind
;
333 VersionTuple DeclVersion
;
334 if (const AvailabilityAttr
*AA
= getAttrForPlatform(S
.Context
, OffendingDecl
))
335 DeclVersion
= AA
->getIntroduced();
337 if (!ShouldDiagnoseAvailabilityInContext(S
, K
, DeclVersion
, Ctx
,
341 SourceLocation Loc
= Locs
.front();
343 // The declaration can have multiple availability attributes, we are looking
345 const AvailabilityAttr
*A
= getAttrForPlatform(S
.Context
, OffendingDecl
);
346 if (A
&& A
->isInherited()) {
347 for (const Decl
*Redecl
= OffendingDecl
->getMostRecentDecl(); Redecl
;
348 Redecl
= Redecl
->getPreviousDecl()) {
349 const AvailabilityAttr
*AForRedecl
=
350 getAttrForPlatform(S
.Context
, Redecl
);
351 if (AForRedecl
&& !AForRedecl
->isInherited()) {
352 // If D is a declaration with inherited attributes, the note should
353 // point to the declaration with actual attributes.
354 NoteLocation
= Redecl
->getLocation();
361 case AR_NotYetIntroduced
: {
362 // We would like to emit the diagnostic even if -Wunguarded-availability is
363 // not specified for deployment targets >= to iOS 11 or equivalent or
364 // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
366 const AvailabilityAttr
*AA
=
367 getAttrForPlatform(S
.getASTContext(), OffendingDecl
);
368 VersionTuple Introduced
= AA
->getIntroduced();
370 bool UseNewWarning
= shouldDiagnoseAvailabilityByDefault(
371 S
.Context
, S
.Context
.getTargetInfo().getPlatformMinVersion(),
373 unsigned Warning
= UseNewWarning
? diag::warn_unguarded_availability_new
374 : diag::warn_unguarded_availability
;
376 std::string
PlatformName(AvailabilityAttr::getPrettyPlatformName(
377 S
.getASTContext().getTargetInfo().getPlatformName()));
379 S
.Diag(Loc
, Warning
) << OffendingDecl
<< PlatformName
380 << Introduced
.getAsString();
382 S
.Diag(OffendingDecl
->getLocation(),
383 diag::note_partial_availability_specified_here
)
384 << OffendingDecl
<< PlatformName
<< Introduced
.getAsString()
385 << S
.Context
.getTargetInfo().getPlatformMinVersion().getAsString();
387 if (const auto *Enclosing
= findEnclosingDeclToAnnotate(Ctx
)) {
388 if (const auto *TD
= dyn_cast
<TagDecl
>(Enclosing
))
389 if (TD
->getDeclName().isEmpty()) {
390 S
.Diag(TD
->getLocation(),
391 diag::note_decl_unguarded_availability_silence
)
392 << /*Anonymous*/ 1 << TD
->getKindName();
396 S
.Diag(Enclosing
->getLocation(),
397 diag::note_decl_unguarded_availability_silence
)
398 << /*Named*/ 0 << Enclosing
;
399 // Don't offer a fixit for declarations with availability attributes.
400 if (Enclosing
->hasAttr
<AvailabilityAttr
>())
402 if (!S
.getPreprocessor().isMacroDefined("API_AVAILABLE"))
404 std::optional
<AttributeInsertion
> Insertion
= createAttributeInsertion(
405 Enclosing
, S
.getSourceManager(), S
.getLangOpts());
408 std::string PlatformName
=
409 AvailabilityAttr::getPlatformNameSourceSpelling(
410 S
.getASTContext().getTargetInfo().getPlatformName())
412 std::string Introduced
=
413 OffendingDecl
->getVersionIntroduced().getAsString();
414 FixitNoteDiag
<< FixItHint::CreateInsertion(
416 (llvm::Twine(Insertion
->Prefix
) + "API_AVAILABLE(" + PlatformName
+
417 "(" + Introduced
+ "))" + Insertion
->Suffix
)
423 diag
= !ObjCPropertyAccess
? diag::warn_deprecated
424 : diag::warn_property_method_deprecated
;
425 diag_message
= diag::warn_deprecated_message
;
426 diag_fwdclass_message
= diag::warn_deprecated_fwdclass_message
;
427 property_note_select
= /* deprecated */ 0;
428 available_here_select_kind
= /* deprecated */ 2;
429 if (const auto *AL
= OffendingDecl
->getAttr
<DeprecatedAttr
>())
430 NoteLocation
= AL
->getLocation();
434 diag
= !ObjCPropertyAccess
? diag::err_unavailable
435 : diag::err_property_method_unavailable
;
436 diag_message
= diag::err_unavailable_message
;
437 diag_fwdclass_message
= diag::warn_unavailable_fwdclass_message
;
438 property_note_select
= /* unavailable */ 1;
439 available_here_select_kind
= /* unavailable */ 0;
441 if (auto AL
= OffendingDecl
->getAttr
<UnavailableAttr
>()) {
442 if (AL
->isImplicit() && AL
->getImplicitReason()) {
443 // Most of these failures are due to extra restrictions in ARC;
444 // reflect that in the primary diagnostic when applicable.
445 auto flagARCError
= [&] {
446 if (S
.getLangOpts().ObjCAutoRefCount
&&
447 S
.getSourceManager().isInSystemHeader(
448 OffendingDecl
->getLocation()))
449 diag
= diag::err_unavailable_in_arc
;
452 switch (AL
->getImplicitReason()) {
453 case UnavailableAttr::IR_None
: break;
455 case UnavailableAttr::IR_ARCForbiddenType
:
457 diag_available_here
= diag::note_arc_forbidden_type
;
460 case UnavailableAttr::IR_ForbiddenWeak
:
461 if (S
.getLangOpts().ObjCWeakRuntime
)
462 diag_available_here
= diag::note_arc_weak_disabled
;
464 diag_available_here
= diag::note_arc_weak_no_runtime
;
467 case UnavailableAttr::IR_ARCForbiddenConversion
:
469 diag_available_here
= diag::note_performs_forbidden_arc_conversion
;
472 case UnavailableAttr::IR_ARCInitReturnsUnrelated
:
474 diag_available_here
= diag::note_arc_init_returns_unrelated
;
477 case UnavailableAttr::IR_ARCFieldWithOwnership
:
479 diag_available_here
= diag::note_arc_field_with_ownership
;
487 llvm_unreachable("Warning for availability of available declaration?");
490 SmallVector
<FixItHint
, 12> FixIts
;
491 if (K
== AR_Deprecated
) {
492 StringRef Replacement
;
493 if (auto AL
= OffendingDecl
->getAttr
<DeprecatedAttr
>())
494 Replacement
= AL
->getReplacement();
495 if (auto AL
= getAttrForPlatform(S
.Context
, OffendingDecl
))
496 Replacement
= AL
->getReplacement();
498 CharSourceRange UseRange
;
499 if (!Replacement
.empty())
501 CharSourceRange::getCharRange(Loc
, S
.getLocForEndOfToken(Loc
));
502 if (UseRange
.isValid()) {
503 if (const auto *MethodDecl
= dyn_cast
<ObjCMethodDecl
>(ReferringDecl
)) {
504 Selector Sel
= MethodDecl
->getSelector();
505 SmallVector
<StringRef
, 12> SelectorSlotNames
;
506 std::optional
<unsigned> NumParams
= tryParseObjCMethodName(
507 Replacement
, SelectorSlotNames
, S
.getLangOpts());
508 if (NumParams
&& *NumParams
== Sel
.getNumArgs()) {
509 assert(SelectorSlotNames
.size() == Locs
.size());
510 for (unsigned I
= 0; I
< Locs
.size(); ++I
) {
511 if (!Sel
.getNameForSlot(I
).empty()) {
512 CharSourceRange NameRange
= CharSourceRange::getCharRange(
513 Locs
[I
], S
.getLocForEndOfToken(Locs
[I
]));
514 FixIts
.push_back(FixItHint::CreateReplacement(
515 NameRange
, SelectorSlotNames
[I
]));
518 FixItHint::CreateInsertion(Locs
[I
], SelectorSlotNames
[I
]));
521 FixIts
.push_back(FixItHint::CreateReplacement(UseRange
, Replacement
));
523 FixIts
.push_back(FixItHint::CreateReplacement(UseRange
, Replacement
));
527 if (!Message
.empty()) {
528 S
.Diag(Loc
, diag_message
) << ReferringDecl
<< Message
<< FixIts
;
530 S
.Diag(ObjCProperty
->getLocation(), diag::note_property_attribute
)
531 << ObjCProperty
->getDeclName() << property_note_select
;
532 } else if (!UnknownObjCClass
) {
533 S
.Diag(Loc
, diag
) << ReferringDecl
<< FixIts
;
535 S
.Diag(ObjCProperty
->getLocation(), diag::note_property_attribute
)
536 << ObjCProperty
->getDeclName() << property_note_select
;
538 S
.Diag(Loc
, diag_fwdclass_message
) << ReferringDecl
<< FixIts
;
539 S
.Diag(UnknownObjCClass
->getLocation(), diag::note_forward_class
);
542 S
.Diag(NoteLocation
, diag_available_here
)
543 << OffendingDecl
<< available_here_select_kind
;
546 void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic
&DD
, Decl
*Ctx
) {
547 assert(DD
.Kind
== DelayedDiagnostic::Availability
&&
548 "Expected an availability diagnostic here");
551 DoEmitAvailabilityWarning(
552 *this, DD
.getAvailabilityResult(), Ctx
, DD
.getAvailabilityReferringDecl(),
553 DD
.getAvailabilityOffendingDecl(), DD
.getAvailabilityMessage(),
554 DD
.getAvailabilitySelectorLocs(), DD
.getUnknownObjCClass(),
555 DD
.getObjCProperty(), false);
558 static void EmitAvailabilityWarning(Sema
&S
, AvailabilityResult AR
,
559 const NamedDecl
*ReferringDecl
,
560 const NamedDecl
*OffendingDecl
,
562 ArrayRef
<SourceLocation
> Locs
,
563 const ObjCInterfaceDecl
*UnknownObjCClass
,
564 const ObjCPropertyDecl
*ObjCProperty
,
565 bool ObjCPropertyAccess
) {
566 // Delay if we're currently parsing a declaration.
567 if (S
.DelayedDiagnostics
.shouldDelayDiagnostics()) {
568 S
.DelayedDiagnostics
.add(
569 DelayedDiagnostic::makeAvailability(
570 AR
, Locs
, ReferringDecl
, OffendingDecl
, UnknownObjCClass
,
571 ObjCProperty
, Message
, ObjCPropertyAccess
));
575 Decl
*Ctx
= cast
<Decl
>(S
.getCurLexicalContext());
576 DoEmitAvailabilityWarning(S
, AR
, Ctx
, ReferringDecl
, OffendingDecl
,
577 Message
, Locs
, UnknownObjCClass
, ObjCProperty
,
583 /// Returns true if the given statement can be a body-like child of \p Parent.
584 bool isBodyLikeChildStmt(const Stmt
*S
, const Stmt
*Parent
) {
585 switch (Parent
->getStmtClass()) {
586 case Stmt::IfStmtClass
:
587 return cast
<IfStmt
>(Parent
)->getThen() == S
||
588 cast
<IfStmt
>(Parent
)->getElse() == S
;
589 case Stmt::WhileStmtClass
:
590 return cast
<WhileStmt
>(Parent
)->getBody() == S
;
591 case Stmt::DoStmtClass
:
592 return cast
<DoStmt
>(Parent
)->getBody() == S
;
593 case Stmt::ForStmtClass
:
594 return cast
<ForStmt
>(Parent
)->getBody() == S
;
595 case Stmt::CXXForRangeStmtClass
:
596 return cast
<CXXForRangeStmt
>(Parent
)->getBody() == S
;
597 case Stmt::ObjCForCollectionStmtClass
:
598 return cast
<ObjCForCollectionStmt
>(Parent
)->getBody() == S
;
599 case Stmt::CaseStmtClass
:
600 case Stmt::DefaultStmtClass
:
601 return cast
<SwitchCase
>(Parent
)->getSubStmt() == S
;
607 class StmtUSEFinder
: public RecursiveASTVisitor
<StmtUSEFinder
> {
611 bool VisitStmt(Stmt
*S
) { return S
!= Target
; }
613 /// Returns true if the given statement is present in the given declaration.
614 static bool isContained(const Stmt
*Target
, const Decl
*D
) {
615 StmtUSEFinder Visitor
;
616 Visitor
.Target
= Target
;
617 return !Visitor
.TraverseDecl(const_cast<Decl
*>(D
));
621 /// Traverses the AST and finds the last statement that used a given
623 class LastDeclUSEFinder
: public RecursiveASTVisitor
<LastDeclUSEFinder
> {
627 bool VisitDeclRefExpr(DeclRefExpr
*DRE
) {
628 if (DRE
->getDecl() == D
)
633 static const Stmt
*findLastStmtThatUsesDecl(const Decl
*D
,
634 const CompoundStmt
*Scope
) {
635 LastDeclUSEFinder Visitor
;
637 for (const Stmt
*S
: llvm::reverse(Scope
->body())) {
638 if (!Visitor
.TraverseStmt(const_cast<Stmt
*>(S
)))
645 /// This class implements -Wunguarded-availability.
647 /// This is done with a traversal of the AST of a function that makes reference
648 /// to a partially available declaration. Whenever we encounter an \c if of the
649 /// form: \c if(@available(...)), we use the version from the condition to visit
650 /// the then statement.
651 class DiagnoseUnguardedAvailability
652 : public RecursiveASTVisitor
<DiagnoseUnguardedAvailability
> {
653 typedef RecursiveASTVisitor
<DiagnoseUnguardedAvailability
> Base
;
658 /// Stack of potentially nested 'if (@available(...))'s.
659 SmallVector
<VersionTuple
, 8> AvailabilityStack
;
660 SmallVector
<const Stmt
*, 16> StmtStack
;
662 void DiagnoseDeclAvailability(NamedDecl
*D
, SourceRange Range
,
663 ObjCInterfaceDecl
*ClassReceiver
= nullptr);
666 DiagnoseUnguardedAvailability(Sema
&SemaRef
, Decl
*Ctx
)
667 : SemaRef(SemaRef
), Ctx(Ctx
) {
668 AvailabilityStack
.push_back(
669 SemaRef
.Context
.getTargetInfo().getPlatformMinVersion());
672 bool TraverseStmt(Stmt
*S
) {
675 StmtStack
.push_back(S
);
676 bool Result
= Base::TraverseStmt(S
);
677 StmtStack
.pop_back();
681 void IssueDiagnostics(Stmt
*S
) { TraverseStmt(S
); }
683 bool TraverseIfStmt(IfStmt
*If
);
685 // for 'case X:' statements, don't bother looking at the 'X'; it can't lead
686 // to any useful diagnostics.
687 bool TraverseCaseStmt(CaseStmt
*CS
) { return TraverseStmt(CS
->getSubStmt()); }
689 bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr
*PRE
) { return true; }
691 bool VisitObjCMessageExpr(ObjCMessageExpr
*Msg
) {
692 if (ObjCMethodDecl
*D
= Msg
->getMethodDecl()) {
693 ObjCInterfaceDecl
*ID
= nullptr;
694 QualType ReceiverTy
= Msg
->getClassReceiver();
695 if (!ReceiverTy
.isNull() && ReceiverTy
->getAsObjCInterfaceType())
696 ID
= ReceiverTy
->getAsObjCInterfaceType()->getInterface();
698 DiagnoseDeclAvailability(
699 D
, SourceRange(Msg
->getSelectorStartLoc(), Msg
->getEndLoc()), ID
);
704 bool VisitDeclRefExpr(DeclRefExpr
*DRE
) {
705 DiagnoseDeclAvailability(DRE
->getDecl(),
706 SourceRange(DRE
->getBeginLoc(), DRE
->getEndLoc()));
710 bool VisitMemberExpr(MemberExpr
*ME
) {
711 DiagnoseDeclAvailability(ME
->getMemberDecl(),
712 SourceRange(ME
->getBeginLoc(), ME
->getEndLoc()));
716 bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr
*E
) {
717 SemaRef
.Diag(E
->getBeginLoc(), diag::warn_at_available_unchecked_use
)
718 << (!SemaRef
.getLangOpts().ObjC
);
722 bool VisitTypeLoc(TypeLoc Ty
);
725 void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
726 NamedDecl
*D
, SourceRange Range
, ObjCInterfaceDecl
*ReceiverClass
) {
727 AvailabilityResult Result
;
728 const NamedDecl
*OffendingDecl
;
729 std::tie(Result
, OffendingDecl
) =
730 ShouldDiagnoseAvailabilityOfDecl(SemaRef
, D
, nullptr, ReceiverClass
);
731 if (Result
!= AR_Available
) {
732 // All other diagnostic kinds have already been handled in
733 // DiagnoseAvailabilityOfDecl.
734 if (Result
!= AR_NotYetIntroduced
)
737 const AvailabilityAttr
*AA
=
738 getAttrForPlatform(SemaRef
.getASTContext(), OffendingDecl
);
739 VersionTuple Introduced
= AA
->getIntroduced();
741 if (AvailabilityStack
.back() >= Introduced
)
744 // If the context of this function is less available than D, we should not
745 // emit a diagnostic.
746 if (!ShouldDiagnoseAvailabilityInContext(SemaRef
, Result
, Introduced
, Ctx
,
750 // We would like to emit the diagnostic even if -Wunguarded-availability is
751 // not specified for deployment targets >= to iOS 11 or equivalent or
752 // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
755 shouldDiagnoseAvailabilityByDefault(
757 SemaRef
.Context
.getTargetInfo().getPlatformMinVersion(), Introduced
)
758 ? diag::warn_unguarded_availability_new
759 : diag::warn_unguarded_availability
;
761 std::string
PlatformName(AvailabilityAttr::getPrettyPlatformName(
762 SemaRef
.getASTContext().getTargetInfo().getPlatformName()));
764 SemaRef
.Diag(Range
.getBegin(), DiagKind
)
765 << Range
<< D
<< PlatformName
<< Introduced
.getAsString();
767 SemaRef
.Diag(OffendingDecl
->getLocation(),
768 diag::note_partial_availability_specified_here
)
769 << OffendingDecl
<< PlatformName
<< Introduced
.getAsString()
770 << SemaRef
.Context
.getTargetInfo()
771 .getPlatformMinVersion()
775 SemaRef
.Diag(Range
.getBegin(), diag::note_unguarded_available_silence
)
777 << (SemaRef
.getLangOpts().ObjC
? /*@available*/ 0
778 : /*__builtin_available*/ 1);
780 // Find the statement which should be enclosed in the if @available check.
781 if (StmtStack
.empty())
783 const Stmt
*StmtOfUse
= StmtStack
.back();
784 const CompoundStmt
*Scope
= nullptr;
785 for (const Stmt
*S
: llvm::reverse(StmtStack
)) {
786 if (const auto *CS
= dyn_cast
<CompoundStmt
>(S
)) {
790 if (isBodyLikeChildStmt(StmtOfUse
, S
)) {
791 // The declaration won't be seen outside of the statement, so we don't
792 // have to wrap the uses of any declared variables in if (@available).
793 // Therefore we can avoid setting Scope here.
798 const Stmt
*LastStmtOfUse
= nullptr;
799 if (isa
<DeclStmt
>(StmtOfUse
) && Scope
) {
800 for (const Decl
*D
: cast
<DeclStmt
>(StmtOfUse
)->decls()) {
801 if (StmtUSEFinder::isContained(StmtStack
.back(), D
)) {
802 LastStmtOfUse
= LastDeclUSEFinder::findLastStmtThatUsesDecl(D
, Scope
);
808 const SourceManager
&SM
= SemaRef
.getSourceManager();
809 SourceLocation IfInsertionLoc
=
810 SM
.getExpansionLoc(StmtOfUse
->getBeginLoc());
811 SourceLocation StmtEndLoc
=
812 SM
.getExpansionRange(
813 (LastStmtOfUse
? LastStmtOfUse
: StmtOfUse
)->getEndLoc())
815 if (SM
.getFileID(IfInsertionLoc
) != SM
.getFileID(StmtEndLoc
))
818 StringRef Indentation
= Lexer::getIndentationForLine(IfInsertionLoc
, SM
);
819 const char *ExtraIndentation
= " ";
820 std::string FixItString
;
821 llvm::raw_string_ostream
FixItOS(FixItString
);
822 FixItOS
<< "if (" << (SemaRef
.getLangOpts().ObjC
? "@available"
823 : "__builtin_available")
825 << AvailabilityAttr::getPlatformNameSourceSpelling(
826 SemaRef
.getASTContext().getTargetInfo().getPlatformName())
827 << " " << Introduced
.getAsString() << ", *)) {\n"
828 << Indentation
<< ExtraIndentation
;
829 FixitDiag
<< FixItHint::CreateInsertion(IfInsertionLoc
, FixItOS
.str());
830 SourceLocation ElseInsertionLoc
= Lexer::findLocationAfterToken(
831 StmtEndLoc
, tok::semi
, SM
, SemaRef
.getLangOpts(),
832 /*SkipTrailingWhitespaceAndNewLine=*/false);
833 if (ElseInsertionLoc
.isInvalid())
835 Lexer::getLocForEndOfToken(StmtEndLoc
, 0, SM
, SemaRef
.getLangOpts());
836 FixItOS
.str().clear();
838 << Indentation
<< "} else {\n"
839 << Indentation
<< ExtraIndentation
840 << "// Fallback on earlier versions\n"
841 << Indentation
<< "}";
842 FixitDiag
<< FixItHint::CreateInsertion(ElseInsertionLoc
, FixItOS
.str());
846 bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty
) {
847 const Type
*TyPtr
= Ty
.getTypePtr();
848 SourceRange Range
{Ty
.getBeginLoc(), Ty
.getEndLoc()};
850 if (Range
.isInvalid())
853 if (const auto *TT
= dyn_cast
<TagType
>(TyPtr
)) {
854 TagDecl
*TD
= TT
->getDecl();
855 DiagnoseDeclAvailability(TD
, Range
);
857 } else if (const auto *TD
= dyn_cast
<TypedefType
>(TyPtr
)) {
858 TypedefNameDecl
*D
= TD
->getDecl();
859 DiagnoseDeclAvailability(D
, Range
);
861 } else if (const auto *ObjCO
= dyn_cast
<ObjCObjectType
>(TyPtr
)) {
862 if (NamedDecl
*D
= ObjCO
->getInterface())
863 DiagnoseDeclAvailability(D
, Range
);
869 bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt
*If
) {
870 VersionTuple CondVersion
;
871 if (auto *E
= dyn_cast
<ObjCAvailabilityCheckExpr
>(If
->getCond())) {
872 CondVersion
= E
->getVersion();
874 // If we're using the '*' case here or if this check is redundant, then we
875 // use the enclosing version to check both branches.
876 if (CondVersion
.empty() || CondVersion
<= AvailabilityStack
.back())
877 return TraverseStmt(If
->getThen()) && TraverseStmt(If
->getElse());
879 // This isn't an availability checking 'if', we can just continue.
880 return Base::TraverseIfStmt(If
);
883 AvailabilityStack
.push_back(CondVersion
);
884 bool ShouldContinue
= TraverseStmt(If
->getThen());
885 AvailabilityStack
.pop_back();
887 return ShouldContinue
&& TraverseStmt(If
->getElse());
890 } // end anonymous namespace
892 void Sema::DiagnoseUnguardedAvailabilityViolations(Decl
*D
) {
893 Stmt
*Body
= nullptr;
895 if (auto *FD
= D
->getAsFunction()) {
896 // FIXME: We only examine the pattern decl for availability violations now,
897 // but we should also examine instantiated templates.
898 if (FD
->isTemplateInstantiation())
901 Body
= FD
->getBody();
903 if (auto *CD
= dyn_cast
<CXXConstructorDecl
>(FD
))
904 for (const CXXCtorInitializer
*CI
: CD
->inits())
905 DiagnoseUnguardedAvailability(*this, D
).IssueDiagnostics(CI
->getInit());
907 } else if (auto *MD
= dyn_cast
<ObjCMethodDecl
>(D
))
908 Body
= MD
->getBody();
909 else if (auto *BD
= dyn_cast
<BlockDecl
>(D
))
910 Body
= BD
->getBody();
912 assert(Body
&& "Need a body here!");
914 DiagnoseUnguardedAvailability(*this, D
).IssueDiagnostics(Body
);
917 FunctionScopeInfo
*Sema::getCurFunctionAvailabilityContext() {
918 if (FunctionScopes
.empty())
921 // Conservatively search the entire current function scope context for
922 // availability violations. This ensures we always correctly analyze nested
923 // classes, blocks, lambdas, etc. that may or may not be inside if(@available)
924 // checks themselves.
925 return FunctionScopes
.front();
928 void Sema::DiagnoseAvailabilityOfDecl(NamedDecl
*D
,
929 ArrayRef
<SourceLocation
> Locs
,
930 const ObjCInterfaceDecl
*UnknownObjCClass
,
931 bool ObjCPropertyAccess
,
932 bool AvoidPartialAvailabilityChecks
,
933 ObjCInterfaceDecl
*ClassReceiver
) {
935 AvailabilityResult Result
;
936 const NamedDecl
* OffendingDecl
;
937 // See if this declaration is unavailable, deprecated, or partial.
938 std::tie(Result
, OffendingDecl
) =
939 ShouldDiagnoseAvailabilityOfDecl(*this, D
, &Message
, ClassReceiver
);
940 if (Result
== AR_Available
)
943 if (Result
== AR_NotYetIntroduced
) {
944 if (AvoidPartialAvailabilityChecks
)
947 // We need to know the @available context in the current function to
948 // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that
949 // when we're done parsing the current function.
950 if (FunctionScopeInfo
*Context
= getCurFunctionAvailabilityContext()) {
951 Context
->HasPotentialAvailabilityViolations
= true;
956 const ObjCPropertyDecl
*ObjCPDecl
= nullptr;
957 if (const auto *MD
= dyn_cast
<ObjCMethodDecl
>(D
)) {
958 if (const ObjCPropertyDecl
*PD
= MD
->findPropertyDecl()) {
959 AvailabilityResult PDeclResult
= PD
->getAvailability(nullptr);
960 if (PDeclResult
== Result
)
965 EmitAvailabilityWarning(*this, Result
, D
, OffendingDecl
, Message
, Locs
,
966 UnknownObjCClass
, ObjCPDecl
, ObjCPropertyAccess
);