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/DeclTemplate.h"
16 #include "clang/AST/DynamicRecursiveASTVisitor.h"
17 #include "clang/AST/ExprObjC.h"
18 #include "clang/AST/StmtObjC.h"
19 #include "clang/Basic/DiagnosticSema.h"
20 #include "clang/Basic/IdentifierTable.h"
21 #include "clang/Basic/LangOptions.h"
22 #include "clang/Basic/TargetInfo.h"
23 #include "clang/Lex/Preprocessor.h"
24 #include "clang/Sema/DelayedDiagnostic.h"
25 #include "clang/Sema/ScopeInfo.h"
26 #include "clang/Sema/Sema.h"
27 #include "clang/Sema/SemaObjC.h"
28 #include "llvm/ADT/StringRef.h"
31 using namespace clang
;
34 static bool hasMatchingEnvironmentOrNone(const ASTContext
&Context
,
35 const AvailabilityAttr
*AA
) {
36 IdentifierInfo
*IIEnvironment
= AA
->getEnvironment();
37 auto Environment
= Context
.getTargetInfo().getTriple().getEnvironment();
38 if (!IIEnvironment
|| Environment
== llvm::Triple::UnknownEnvironment
)
41 llvm::Triple::EnvironmentType ET
=
42 AvailabilityAttr::getEnvironmentType(IIEnvironment
->getName());
43 return Environment
== ET
;
46 static const AvailabilityAttr
*getAttrForPlatform(ASTContext
&Context
,
48 AvailabilityAttr
const *PartialMatch
= nullptr;
49 // Check each AvailabilityAttr to find the one for this platform.
50 // For multiple attributes with the same platform try to find one for this
52 // The attribute is always on the FunctionDecl, not on the
53 // FunctionTemplateDecl.
54 if (const auto *FTD
= dyn_cast
<FunctionTemplateDecl
>(D
))
55 D
= FTD
->getTemplatedDecl();
56 for (const auto *A
: D
->attrs()) {
57 if (const auto *Avail
= dyn_cast
<AvailabilityAttr
>(A
)) {
58 // FIXME: this is copied from CheckAvailability. We should try to
61 // Check if this is an App Extension "platform", and if so chop off
62 // the suffix for matching with the actual platform.
63 StringRef ActualPlatform
= Avail
->getPlatform()->getName();
64 StringRef RealizedPlatform
= ActualPlatform
;
65 if (Context
.getLangOpts().AppExt
) {
66 size_t suffix
= RealizedPlatform
.rfind("_app_extension");
67 if (suffix
!= StringRef::npos
)
68 RealizedPlatform
= RealizedPlatform
.slice(0, suffix
);
71 StringRef TargetPlatform
= Context
.getTargetInfo().getPlatformName();
73 // Match the platform name.
74 if (RealizedPlatform
== TargetPlatform
) {
75 // Find the best matching attribute for this environment
76 if (hasMatchingEnvironmentOrNone(Context
, Avail
))
85 /// The diagnostic we should emit for \c D, and the declaration that
86 /// originated it, or \c AR_Available.
88 /// \param D The declaration to check.
89 /// \param Message If non-null, this will be populated with the message from
90 /// the availability attribute that is selected.
91 /// \param ClassReceiver If we're checking the method of a class message
92 /// send, the class. Otherwise nullptr.
93 static std::pair
<AvailabilityResult
, const NamedDecl
*>
94 ShouldDiagnoseAvailabilityOfDecl(Sema
&S
, const NamedDecl
*D
,
96 ObjCInterfaceDecl
*ClassReceiver
) {
97 AvailabilityResult Result
= D
->getAvailability(Message
);
99 // For typedefs, if the typedef declaration appears available look
100 // to the underlying type to see if it is more restrictive.
101 while (const auto *TD
= dyn_cast
<TypedefNameDecl
>(D
)) {
102 if (Result
== AR_Available
) {
103 if (const auto *TT
= TD
->getUnderlyingType()->getAs
<TagType
>()) {
105 Result
= D
->getAvailability(Message
);
112 // For alias templates, get the underlying declaration.
113 if (const auto *ADecl
= dyn_cast
<TypeAliasTemplateDecl
>(D
)) {
114 D
= ADecl
->getTemplatedDecl();
115 Result
= D
->getAvailability(Message
);
118 // Forward class declarations get their attributes from their definition.
119 if (const auto *IDecl
= dyn_cast
<ObjCInterfaceDecl
>(D
)) {
120 if (IDecl
->getDefinition()) {
121 D
= IDecl
->getDefinition();
122 Result
= D
->getAvailability(Message
);
126 if (const auto *ECD
= dyn_cast
<EnumConstantDecl
>(D
))
127 if (Result
== AR_Available
) {
128 const DeclContext
*DC
= ECD
->getDeclContext();
129 if (const auto *TheEnumDecl
= dyn_cast
<EnumDecl
>(DC
)) {
130 Result
= TheEnumDecl
->getAvailability(Message
);
135 // For +new, infer availability from -init.
136 if (const auto *MD
= dyn_cast
<ObjCMethodDecl
>(D
)) {
137 if (S
.ObjC().NSAPIObj
&& ClassReceiver
) {
138 ObjCMethodDecl
*Init
= ClassReceiver
->lookupInstanceMethod(
139 S
.ObjC().NSAPIObj
->getInitSelector());
140 if (Init
&& Result
== AR_Available
&& MD
->isClassMethod() &&
141 MD
->getSelector() == S
.ObjC().NSAPIObj
->getNewSelector() &&
142 MD
->definedInNSObject(S
.getASTContext())) {
143 Result
= Init
->getAvailability(Message
);
153 /// whether we should emit a diagnostic for \c K and \c DeclVersion in
154 /// the context of \c Ctx. For example, we should emit an unavailable diagnostic
155 /// in a deprecated context, but not the other way around.
156 static bool ShouldDiagnoseAvailabilityInContext(
157 Sema
&S
, AvailabilityResult K
, VersionTuple DeclVersion
,
158 const IdentifierInfo
*DeclEnv
, Decl
*Ctx
, const NamedDecl
*OffendingDecl
) {
159 assert(K
!= AR_Available
&& "Expected an unavailable declaration here!");
161 // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic.
162 auto DeclLoc
= Ctx
->getBeginLoc();
163 // This is only a problem in Foundation's C++ implementation for CF_OPTIONS.
164 if (DeclLoc
.isMacroID() && S
.getLangOpts().CPlusPlus
&&
165 isa
<TypedefDecl
>(OffendingDecl
)) {
166 StringRef MacroName
= S
.getPreprocessor().getImmediateMacroName(DeclLoc
);
167 if (MacroName
== "CF_OPTIONS" || MacroName
== "OBJC_OPTIONS" ||
168 MacroName
== "SWIFT_OPTIONS" || MacroName
== "NS_OPTIONS") {
173 // In HLSL, skip emitting diagnostic if the diagnostic mode is not set to
174 // strict (-fhlsl-strict-availability), or if the target is library and the
175 // availability is restricted to a specific environment/shader stage.
176 // For libraries the availability will be checked later in
177 // DiagnoseHLSLAvailability class once where the specific environment/shader
178 // stage of the caller is known.
179 if (S
.getLangOpts().HLSL
) {
180 if (!S
.getLangOpts().HLSLStrictAvailability
||
181 (DeclEnv
!= nullptr &&
182 S
.getASTContext().getTargetInfo().getTriple().getEnvironment() ==
183 llvm::Triple::EnvironmentType::Library
))
187 if (K
== AR_Deprecated
) {
188 if (const auto *VD
= dyn_cast
<VarDecl
>(OffendingDecl
))
189 if (VD
->isLocalVarDeclOrParm() && VD
->isDeprecated())
193 // Checks if we should emit the availability diagnostic in the context of C.
194 auto CheckContext
= [&](const Decl
*C
) {
195 if (K
== AR_NotYetIntroduced
) {
196 if (const AvailabilityAttr
*AA
= getAttrForPlatform(S
.Context
, C
))
197 if (AA
->getIntroduced() >= DeclVersion
&&
198 AA
->getEnvironment() == DeclEnv
)
200 } else if (K
== AR_Deprecated
) {
201 if (C
->isDeprecated())
203 } else if (K
== AR_Unavailable
) {
204 // It is perfectly fine to refer to an 'unavailable' Objective-C method
205 // when it is referenced from within the @implementation itself. In this
206 // context, we interpret unavailable as a form of access control.
207 if (const auto *MD
= dyn_cast
<ObjCMethodDecl
>(OffendingDecl
)) {
208 if (const auto *Impl
= dyn_cast
<ObjCImplDecl
>(C
)) {
209 if (MD
->getClassInterface() == Impl
->getClassInterface())
215 if (C
->isUnavailable())
221 if (CheckContext(Ctx
))
224 // An implementation implicitly has the availability of the interface.
225 // Unless it is "+load" method.
226 if (const auto *MethodD
= dyn_cast
<ObjCMethodDecl
>(Ctx
))
227 if (MethodD
->isClassMethod() &&
228 MethodD
->getSelector().getAsString() == "load")
231 if (const auto *CatOrImpl
= dyn_cast
<ObjCImplDecl
>(Ctx
)) {
232 if (const ObjCInterfaceDecl
*Interface
= CatOrImpl
->getClassInterface())
233 if (CheckContext(Interface
))
236 // A category implicitly has the availability of the interface.
237 else if (const auto *CatD
= dyn_cast
<ObjCCategoryDecl
>(Ctx
))
238 if (const ObjCInterfaceDecl
*Interface
= CatD
->getClassInterface())
239 if (CheckContext(Interface
))
241 } while ((Ctx
= cast_or_null
<Decl
>(Ctx
->getDeclContext())));
246 static unsigned getAvailabilityDiagnosticKind(
247 const ASTContext
&Context
, const VersionTuple
&DeploymentVersion
,
248 const VersionTuple
&DeclVersion
, bool HasMatchingEnv
) {
249 const auto &Triple
= Context
.getTargetInfo().getTriple();
250 VersionTuple ForceAvailabilityFromVersion
;
251 switch (Triple
.getOS()) {
252 // For iOS, emit the diagnostic even if -Wunguarded-availability is
253 // not specified for deployment targets >= to iOS 11 or equivalent or
254 // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
256 case llvm::Triple::IOS
:
257 case llvm::Triple::TvOS
:
258 ForceAvailabilityFromVersion
= VersionTuple(/*Major=*/11);
260 case llvm::Triple::WatchOS
:
261 ForceAvailabilityFromVersion
= VersionTuple(/*Major=*/4);
263 case llvm::Triple::Darwin
:
264 case llvm::Triple::MacOSX
:
265 ForceAvailabilityFromVersion
= VersionTuple(/*Major=*/10, /*Minor=*/13);
267 // For HLSL, use diagnostic from HLSLAvailability group which
268 // are reported as errors by default and in strict diagnostic mode
269 // (-fhlsl-strict-availability) and as warnings in relaxed diagnostic
270 // mode (-Wno-error=hlsl-availability)
271 case llvm::Triple::ShaderModel
:
272 return HasMatchingEnv
? diag::warn_hlsl_availability
273 : diag::warn_hlsl_availability_unavailable
;
275 // New Apple targets should always warn about availability.
276 ForceAvailabilityFromVersion
=
277 (Triple
.getVendor() == llvm::Triple::Apple
)
278 ? VersionTuple(/*Major=*/0, 0)
279 : VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1);
281 if (DeploymentVersion
>= ForceAvailabilityFromVersion
||
282 DeclVersion
>= ForceAvailabilityFromVersion
)
283 return HasMatchingEnv
? diag::warn_unguarded_availability_new
284 : diag::warn_unguarded_availability_unavailable_new
;
285 return HasMatchingEnv
? diag::warn_unguarded_availability
286 : diag::warn_unguarded_availability_unavailable
;
289 static NamedDecl
*findEnclosingDeclToAnnotate(Decl
*OrigCtx
) {
290 for (Decl
*Ctx
= OrigCtx
; Ctx
;
291 Ctx
= cast_or_null
<Decl
>(Ctx
->getDeclContext())) {
292 if (isa
<TagDecl
>(Ctx
) || isa
<FunctionDecl
>(Ctx
) || isa
<ObjCMethodDecl
>(Ctx
))
293 return cast
<NamedDecl
>(Ctx
);
294 if (auto *CD
= dyn_cast
<ObjCContainerDecl
>(Ctx
)) {
295 if (auto *Imp
= dyn_cast
<ObjCImplDecl
>(Ctx
))
296 return Imp
->getClassInterface();
301 return dyn_cast
<NamedDecl
>(OrigCtx
);
306 struct AttributeInsertion
{
311 static AttributeInsertion
createInsertionAfter(const NamedDecl
*D
) {
312 return {" ", D
->getEndLoc(), ""};
314 static AttributeInsertion
createInsertionAfter(SourceLocation Loc
) {
315 return {" ", Loc
, ""};
317 static AttributeInsertion
createInsertionBefore(const NamedDecl
*D
) {
318 return {"", D
->getBeginLoc(), "\n"};
322 } // end anonymous namespace
324 /// Tries to parse a string as ObjC method name.
326 /// \param Name The string to parse. Expected to originate from availability
327 /// attribute argument.
328 /// \param SlotNames The vector that will be populated with slot names. In case
329 /// of unsuccessful parsing can contain invalid data.
330 /// \returns A number of method parameters if parsing was successful,
331 /// std::nullopt otherwise.
332 static std::optional
<unsigned>
333 tryParseObjCMethodName(StringRef Name
, SmallVectorImpl
<StringRef
> &SlotNames
,
334 const LangOptions
&LangOpts
) {
335 // Accept replacements starting with - or + as valid ObjC method names.
336 if (!Name
.empty() && (Name
.front() == '-' || Name
.front() == '+'))
337 Name
= Name
.drop_front(1);
340 Name
.split(SlotNames
, ':');
342 if (Name
.back() == ':') {
343 // Remove an empty string at the end that doesn't represent any slot.
344 SlotNames
.pop_back();
345 NumParams
= SlotNames
.size();
347 if (SlotNames
.size() != 1)
348 // Not a valid method name, just a colon-separated string.
352 // Verify all slot names are valid.
353 bool AllowDollar
= LangOpts
.DollarIdents
;
354 for (StringRef S
: SlotNames
) {
357 if (!isValidAsciiIdentifier(S
, AllowDollar
))
363 /// Returns a source location in which it's appropriate to insert a new
364 /// attribute for the given declaration \D.
365 static std::optional
<AttributeInsertion
>
366 createAttributeInsertion(const NamedDecl
*D
, const SourceManager
&SM
,
367 const LangOptions
&LangOpts
) {
368 if (isa
<ObjCPropertyDecl
>(D
))
369 return AttributeInsertion::createInsertionAfter(D
);
370 if (const auto *MD
= dyn_cast
<ObjCMethodDecl
>(D
)) {
373 return AttributeInsertion::createInsertionAfter(D
);
375 if (const auto *TD
= dyn_cast
<TagDecl
>(D
)) {
377 Lexer::getLocForEndOfToken(TD
->getInnerLocStart(), 0, SM
, LangOpts
);
380 // Insert after the 'struct'/whatever keyword.
381 return AttributeInsertion::createInsertionAfter(Loc
);
383 return AttributeInsertion::createInsertionBefore(D
);
386 /// Actually emit an availability diagnostic for a reference to an unavailable
389 /// \param Ctx The context that the reference occurred in
390 /// \param ReferringDecl The exact declaration that was referenced.
391 /// \param OffendingDecl A related decl to \c ReferringDecl that has an
392 /// availability attribute corresponding to \c K attached to it. Note that this
393 /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and
394 /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl
395 /// and OffendingDecl is the EnumDecl.
396 static void DoEmitAvailabilityWarning(Sema
&S
, AvailabilityResult K
,
397 Decl
*Ctx
, const NamedDecl
*ReferringDecl
,
398 const NamedDecl
*OffendingDecl
,
400 ArrayRef
<SourceLocation
> Locs
,
401 const ObjCInterfaceDecl
*UnknownObjCClass
,
402 const ObjCPropertyDecl
*ObjCProperty
,
403 bool ObjCPropertyAccess
) {
404 // Diagnostics for deprecated or unavailable.
405 unsigned diag
, diag_message
, diag_fwdclass_message
;
406 unsigned diag_available_here
= diag::note_availability_specified_here
;
407 SourceLocation NoteLocation
= OffendingDecl
->getLocation();
409 // Matches 'diag::note_property_attribute' options.
410 unsigned property_note_select
;
412 // Matches diag::note_availability_specified_here.
413 unsigned available_here_select_kind
;
415 VersionTuple DeclVersion
;
416 const AvailabilityAttr
*AA
= getAttrForPlatform(S
.Context
, OffendingDecl
);
417 const IdentifierInfo
*IIEnv
= nullptr;
419 DeclVersion
= AA
->getIntroduced();
420 IIEnv
= AA
->getEnvironment();
423 if (!ShouldDiagnoseAvailabilityInContext(S
, K
, DeclVersion
, IIEnv
, Ctx
,
427 SourceLocation Loc
= Locs
.front();
429 // The declaration can have multiple availability attributes, we are looking
431 if (AA
&& AA
->isInherited()) {
432 for (const Decl
*Redecl
= OffendingDecl
->getMostRecentDecl(); Redecl
;
433 Redecl
= Redecl
->getPreviousDecl()) {
434 const AvailabilityAttr
*AForRedecl
=
435 getAttrForPlatform(S
.Context
, Redecl
);
436 if (AForRedecl
&& !AForRedecl
->isInherited()) {
437 // If D is a declaration with inherited attributes, the note should
438 // point to the declaration with actual attributes.
439 NoteLocation
= Redecl
->getLocation();
446 case AR_NotYetIntroduced
: {
447 // We would like to emit the diagnostic even if -Wunguarded-availability is
448 // not specified for deployment targets >= to iOS 11 or equivalent or
449 // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
451 assert(AA
!= nullptr && "expecting valid availability attribute");
452 VersionTuple Introduced
= AA
->getIntroduced();
453 bool EnvironmentMatchesOrNone
=
454 hasMatchingEnvironmentOrNone(S
.getASTContext(), AA
);
456 const TargetInfo
&TI
= S
.getASTContext().getTargetInfo();
457 std::string
PlatformName(
458 AvailabilityAttr::getPrettyPlatformName(TI
.getPlatformName()));
459 llvm::StringRef
TargetEnvironment(
460 llvm::Triple::getEnvironmentTypeName(TI
.getTriple().getEnvironment()));
461 llvm::StringRef AttrEnvironment
=
462 AA
->getEnvironment() ? AA
->getEnvironment()->getName() : "";
463 bool UseEnvironment
=
464 (!AttrEnvironment
.empty() && !TargetEnvironment
.empty());
466 unsigned DiagKind
= getAvailabilityDiagnosticKind(
467 S
.Context
, S
.Context
.getTargetInfo().getPlatformMinVersion(),
468 Introduced
, EnvironmentMatchesOrNone
);
470 S
.Diag(Loc
, DiagKind
) << OffendingDecl
<< PlatformName
471 << Introduced
.getAsString() << UseEnvironment
472 << TargetEnvironment
;
474 S
.Diag(OffendingDecl
->getLocation(),
475 diag::note_partial_availability_specified_here
)
476 << OffendingDecl
<< PlatformName
<< Introduced
.getAsString()
477 << S
.Context
.getTargetInfo().getPlatformMinVersion().getAsString()
478 << UseEnvironment
<< AttrEnvironment
<< TargetEnvironment
;
480 // Do not offer to silence the warning or fixits for HLSL
481 if (S
.getLangOpts().HLSL
)
484 if (const auto *Enclosing
= findEnclosingDeclToAnnotate(Ctx
)) {
485 if (const auto *TD
= dyn_cast
<TagDecl
>(Enclosing
))
486 if (TD
->getDeclName().isEmpty()) {
487 S
.Diag(TD
->getLocation(),
488 diag::note_decl_unguarded_availability_silence
)
489 << /*Anonymous*/ 1 << TD
->getKindName();
493 S
.Diag(Enclosing
->getLocation(),
494 diag::note_decl_unguarded_availability_silence
)
495 << /*Named*/ 0 << Enclosing
;
496 // Don't offer a fixit for declarations with availability attributes.
497 if (Enclosing
->hasAttr
<AvailabilityAttr
>())
499 Preprocessor
&PP
= S
.getPreprocessor();
500 if (!PP
.isMacroDefined("API_AVAILABLE"))
502 std::optional
<AttributeInsertion
> Insertion
= createAttributeInsertion(
503 Enclosing
, S
.getSourceManager(), S
.getLangOpts());
506 StringRef PlatformName
=
507 S
.getASTContext().getTargetInfo().getPlatformName();
509 // Apple's API_AVAILABLE macro expands roughly like this.
510 // API_AVAILABLE(ios(17.0))
511 // __attribute__((availability(__API_AVAILABLE_PLATFORM_ios(17.0)))
512 // __attribute__((availability(ios,introduced=17.0)))
513 // In order to figure out which platform name to use in the API_AVAILABLE
514 // macro, the associated __API_AVAILABLE_PLATFORM_ macro needs to be
515 // found. The __API_AVAILABLE_PLATFORM_ macros aren't consistent about
516 // using the canonical platform name, source spelling name, or one of the
517 // other supported names (i.e. one of the keys in canonicalizePlatformName
518 // that's neither). Check all of the supported names for a match.
519 std::vector
<StringRef
> EquivalentPlatforms
=
520 AvailabilityAttr::equivalentPlatformNames(PlatformName
);
521 llvm::Twine MacroPrefix
= "__API_AVAILABLE_PLATFORM_";
522 auto AvailablePlatform
=
523 llvm::find_if(EquivalentPlatforms
, [&](StringRef EquivalentPlatform
) {
524 return PP
.isMacroDefined((MacroPrefix
+ EquivalentPlatform
).str());
526 if (AvailablePlatform
== EquivalentPlatforms
.end())
528 std::string Introduced
=
529 OffendingDecl
->getVersionIntroduced().getAsString();
530 FixitNoteDiag
<< FixItHint::CreateInsertion(
532 (llvm::Twine(Insertion
->Prefix
) + "API_AVAILABLE(" +
533 *AvailablePlatform
+ "(" + Introduced
+ "))" + Insertion
->Suffix
)
539 diag
= !ObjCPropertyAccess
? diag::warn_deprecated
540 : diag::warn_property_method_deprecated
;
541 diag_message
= diag::warn_deprecated_message
;
542 diag_fwdclass_message
= diag::warn_deprecated_fwdclass_message
;
543 property_note_select
= /* deprecated */ 0;
544 available_here_select_kind
= /* deprecated */ 2;
545 if (const auto *AL
= OffendingDecl
->getAttr
<DeprecatedAttr
>())
546 NoteLocation
= AL
->getLocation();
550 diag
= !ObjCPropertyAccess
? diag::err_unavailable
551 : diag::err_property_method_unavailable
;
552 diag_message
= diag::err_unavailable_message
;
553 diag_fwdclass_message
= diag::warn_unavailable_fwdclass_message
;
554 property_note_select
= /* unavailable */ 1;
555 available_here_select_kind
= /* unavailable */ 0;
557 if (auto AL
= OffendingDecl
->getAttr
<UnavailableAttr
>()) {
558 if (AL
->isImplicit() && AL
->getImplicitReason()) {
559 // Most of these failures are due to extra restrictions in ARC;
560 // reflect that in the primary diagnostic when applicable.
561 auto flagARCError
= [&] {
562 if (S
.getLangOpts().ObjCAutoRefCount
&&
563 S
.getSourceManager().isInSystemHeader(
564 OffendingDecl
->getLocation()))
565 diag
= diag::err_unavailable_in_arc
;
568 switch (AL
->getImplicitReason()) {
569 case UnavailableAttr::IR_None
: break;
571 case UnavailableAttr::IR_ARCForbiddenType
:
573 diag_available_here
= diag::note_arc_forbidden_type
;
576 case UnavailableAttr::IR_ForbiddenWeak
:
577 if (S
.getLangOpts().ObjCWeakRuntime
)
578 diag_available_here
= diag::note_arc_weak_disabled
;
580 diag_available_here
= diag::note_arc_weak_no_runtime
;
583 case UnavailableAttr::IR_ARCForbiddenConversion
:
585 diag_available_here
= diag::note_performs_forbidden_arc_conversion
;
588 case UnavailableAttr::IR_ARCInitReturnsUnrelated
:
590 diag_available_here
= diag::note_arc_init_returns_unrelated
;
593 case UnavailableAttr::IR_ARCFieldWithOwnership
:
595 diag_available_here
= diag::note_arc_field_with_ownership
;
603 llvm_unreachable("Warning for availability of available declaration?");
606 SmallVector
<FixItHint
, 12> FixIts
;
607 if (K
== AR_Deprecated
) {
608 StringRef Replacement
;
609 if (auto AL
= OffendingDecl
->getAttr
<DeprecatedAttr
>())
610 Replacement
= AL
->getReplacement();
611 if (auto AL
= getAttrForPlatform(S
.Context
, OffendingDecl
))
612 Replacement
= AL
->getReplacement();
614 CharSourceRange UseRange
;
615 if (!Replacement
.empty())
617 CharSourceRange::getCharRange(Loc
, S
.getLocForEndOfToken(Loc
));
618 if (UseRange
.isValid()) {
619 if (const auto *MethodDecl
= dyn_cast
<ObjCMethodDecl
>(ReferringDecl
)) {
620 Selector Sel
= MethodDecl
->getSelector();
621 SmallVector
<StringRef
, 12> SelectorSlotNames
;
622 std::optional
<unsigned> NumParams
= tryParseObjCMethodName(
623 Replacement
, SelectorSlotNames
, S
.getLangOpts());
624 if (NumParams
&& *NumParams
== Sel
.getNumArgs()) {
625 assert(SelectorSlotNames
.size() == Locs
.size());
626 for (unsigned I
= 0; I
< Locs
.size(); ++I
) {
627 if (!Sel
.getNameForSlot(I
).empty()) {
628 CharSourceRange NameRange
= CharSourceRange::getCharRange(
629 Locs
[I
], S
.getLocForEndOfToken(Locs
[I
]));
630 FixIts
.push_back(FixItHint::CreateReplacement(
631 NameRange
, SelectorSlotNames
[I
]));
634 FixItHint::CreateInsertion(Locs
[I
], SelectorSlotNames
[I
]));
637 FixIts
.push_back(FixItHint::CreateReplacement(UseRange
, Replacement
));
639 FixIts
.push_back(FixItHint::CreateReplacement(UseRange
, Replacement
));
643 // We emit deprecation warning for deprecated specializations
644 // when their instantiation stacks originate outside
645 // of a system header, even if the diagnostics is suppresed at the
646 // point of definition.
647 SourceLocation InstantiationLoc
=
648 S
.getTopMostPointOfInstantiation(ReferringDecl
);
649 bool ShouldAllowWarningInSystemHeader
=
650 InstantiationLoc
!= Loc
&&
651 !S
.getSourceManager().isInSystemHeader(InstantiationLoc
);
652 struct AllowWarningInSystemHeaders
{
653 AllowWarningInSystemHeaders(DiagnosticsEngine
&E
,
654 bool AllowWarningInSystemHeaders
)
655 : Engine(E
), Prev(E
.getSuppressSystemWarnings()) {
656 E
.setSuppressSystemWarnings(!AllowWarningInSystemHeaders
);
658 ~AllowWarningInSystemHeaders() { Engine
.setSuppressSystemWarnings(Prev
); }
661 DiagnosticsEngine
&Engine
;
663 } SystemWarningOverrideRAII(S
.getDiagnostics(),
664 ShouldAllowWarningInSystemHeader
);
666 if (!Message
.empty()) {
667 S
.Diag(Loc
, diag_message
) << ReferringDecl
<< Message
<< FixIts
;
669 S
.Diag(ObjCProperty
->getLocation(), diag::note_property_attribute
)
670 << ObjCProperty
->getDeclName() << property_note_select
;
671 } else if (!UnknownObjCClass
) {
672 S
.Diag(Loc
, diag
) << ReferringDecl
<< FixIts
;
674 S
.Diag(ObjCProperty
->getLocation(), diag::note_property_attribute
)
675 << ObjCProperty
->getDeclName() << property_note_select
;
677 S
.Diag(Loc
, diag_fwdclass_message
) << ReferringDecl
<< FixIts
;
678 S
.Diag(UnknownObjCClass
->getLocation(), diag::note_forward_class
);
681 S
.Diag(NoteLocation
, diag_available_here
)
682 << OffendingDecl
<< available_here_select_kind
;
685 void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic
&DD
, Decl
*Ctx
) {
686 assert(DD
.Kind
== DelayedDiagnostic::Availability
&&
687 "Expected an availability diagnostic here");
690 DoEmitAvailabilityWarning(
691 *this, DD
.getAvailabilityResult(), Ctx
, DD
.getAvailabilityReferringDecl(),
692 DD
.getAvailabilityOffendingDecl(), DD
.getAvailabilityMessage(),
693 DD
.getAvailabilitySelectorLocs(), DD
.getUnknownObjCClass(),
694 DD
.getObjCProperty(), false);
697 static void EmitAvailabilityWarning(Sema
&S
, AvailabilityResult AR
,
698 const NamedDecl
*ReferringDecl
,
699 const NamedDecl
*OffendingDecl
,
701 ArrayRef
<SourceLocation
> Locs
,
702 const ObjCInterfaceDecl
*UnknownObjCClass
,
703 const ObjCPropertyDecl
*ObjCProperty
,
704 bool ObjCPropertyAccess
) {
705 // Delay if we're currently parsing a declaration.
706 if (S
.DelayedDiagnostics
.shouldDelayDiagnostics()) {
707 S
.DelayedDiagnostics
.add(
708 DelayedDiagnostic::makeAvailability(
709 AR
, Locs
, ReferringDecl
, OffendingDecl
, UnknownObjCClass
,
710 ObjCProperty
, Message
, ObjCPropertyAccess
));
714 Decl
*Ctx
= cast
<Decl
>(S
.getCurLexicalContext());
715 DoEmitAvailabilityWarning(S
, AR
, Ctx
, ReferringDecl
, OffendingDecl
,
716 Message
, Locs
, UnknownObjCClass
, ObjCProperty
,
722 /// Returns true if the given statement can be a body-like child of \p Parent.
723 bool isBodyLikeChildStmt(const Stmt
*S
, const Stmt
*Parent
) {
724 switch (Parent
->getStmtClass()) {
725 case Stmt::IfStmtClass
:
726 return cast
<IfStmt
>(Parent
)->getThen() == S
||
727 cast
<IfStmt
>(Parent
)->getElse() == S
;
728 case Stmt::WhileStmtClass
:
729 return cast
<WhileStmt
>(Parent
)->getBody() == S
;
730 case Stmt::DoStmtClass
:
731 return cast
<DoStmt
>(Parent
)->getBody() == S
;
732 case Stmt::ForStmtClass
:
733 return cast
<ForStmt
>(Parent
)->getBody() == S
;
734 case Stmt::CXXForRangeStmtClass
:
735 return cast
<CXXForRangeStmt
>(Parent
)->getBody() == S
;
736 case Stmt::ObjCForCollectionStmtClass
:
737 return cast
<ObjCForCollectionStmt
>(Parent
)->getBody() == S
;
738 case Stmt::CaseStmtClass
:
739 case Stmt::DefaultStmtClass
:
740 return cast
<SwitchCase
>(Parent
)->getSubStmt() == S
;
746 class StmtUSEFinder
: public DynamicRecursiveASTVisitor
{
750 bool VisitStmt(Stmt
*S
) override
{ return S
!= Target
; }
752 /// Returns true if the given statement is present in the given declaration.
753 static bool isContained(const Stmt
*Target
, const Decl
*D
) {
754 StmtUSEFinder Visitor
;
755 Visitor
.Target
= Target
;
756 return !Visitor
.TraverseDecl(const_cast<Decl
*>(D
));
760 /// Traverses the AST and finds the last statement that used a given
762 class LastDeclUSEFinder
: public DynamicRecursiveASTVisitor
{
766 bool VisitDeclRefExpr(DeclRefExpr
*DRE
) override
{
767 if (DRE
->getDecl() == D
)
772 static const Stmt
*findLastStmtThatUsesDecl(const Decl
*D
,
773 const CompoundStmt
*Scope
) {
774 LastDeclUSEFinder Visitor
;
776 for (const Stmt
*S
: llvm::reverse(Scope
->body())) {
777 if (!Visitor
.TraverseStmt(const_cast<Stmt
*>(S
)))
784 /// This class implements -Wunguarded-availability.
786 /// This is done with a traversal of the AST of a function that makes reference
787 /// to a partially available declaration. Whenever we encounter an \c if of the
788 /// form: \c if(@available(...)), we use the version from the condition to visit
789 /// the then statement.
790 class DiagnoseUnguardedAvailability
: public DynamicRecursiveASTVisitor
{
794 /// Stack of potentially nested 'if (@available(...))'s.
795 SmallVector
<VersionTuple
, 8> AvailabilityStack
;
796 SmallVector
<const Stmt
*, 16> StmtStack
;
798 void DiagnoseDeclAvailability(NamedDecl
*D
, SourceRange Range
,
799 ObjCInterfaceDecl
*ClassReceiver
= nullptr);
802 DiagnoseUnguardedAvailability(Sema
&SemaRef
, Decl
*Ctx
)
803 : SemaRef(SemaRef
), Ctx(Ctx
) {
804 AvailabilityStack
.push_back(
805 SemaRef
.Context
.getTargetInfo().getPlatformMinVersion());
808 bool TraverseStmt(Stmt
*S
) override
{
811 StmtStack
.push_back(S
);
812 bool Result
= DynamicRecursiveASTVisitor::TraverseStmt(S
);
813 StmtStack
.pop_back();
817 void IssueDiagnostics(Stmt
*S
) { TraverseStmt(S
); }
819 bool TraverseIfStmt(IfStmt
*If
) override
;
821 // for 'case X:' statements, don't bother looking at the 'X'; it can't lead
822 // to any useful diagnostics.
823 bool TraverseCaseStmt(CaseStmt
*CS
) override
{
824 return TraverseStmt(CS
->getSubStmt());
827 bool VisitObjCMessageExpr(ObjCMessageExpr
*Msg
) override
{
828 if (ObjCMethodDecl
*D
= Msg
->getMethodDecl()) {
829 ObjCInterfaceDecl
*ID
= nullptr;
830 QualType ReceiverTy
= Msg
->getClassReceiver();
831 if (!ReceiverTy
.isNull() && ReceiverTy
->getAsObjCInterfaceType())
832 ID
= ReceiverTy
->getAsObjCInterfaceType()->getInterface();
834 DiagnoseDeclAvailability(
835 D
, SourceRange(Msg
->getSelectorStartLoc(), Msg
->getEndLoc()), ID
);
840 bool VisitDeclRefExpr(DeclRefExpr
*DRE
) override
{
841 DiagnoseDeclAvailability(DRE
->getDecl(),
842 SourceRange(DRE
->getBeginLoc(), DRE
->getEndLoc()));
846 bool VisitMemberExpr(MemberExpr
*ME
) override
{
847 DiagnoseDeclAvailability(ME
->getMemberDecl(),
848 SourceRange(ME
->getBeginLoc(), ME
->getEndLoc()));
852 bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr
*E
) override
{
853 SemaRef
.Diag(E
->getBeginLoc(), diag::warn_at_available_unchecked_use
)
854 << (!SemaRef
.getLangOpts().ObjC
);
858 bool VisitTypeLoc(TypeLoc Ty
) override
;
861 void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
862 NamedDecl
*D
, SourceRange Range
, ObjCInterfaceDecl
*ReceiverClass
) {
863 AvailabilityResult Result
;
864 const NamedDecl
*OffendingDecl
;
865 std::tie(Result
, OffendingDecl
) =
866 ShouldDiagnoseAvailabilityOfDecl(SemaRef
, D
, nullptr, ReceiverClass
);
867 if (Result
!= AR_Available
) {
868 // All other diagnostic kinds have already been handled in
869 // DiagnoseAvailabilityOfDecl.
870 if (Result
!= AR_NotYetIntroduced
)
873 const AvailabilityAttr
*AA
=
874 getAttrForPlatform(SemaRef
.getASTContext(), OffendingDecl
);
875 assert(AA
!= nullptr && "expecting valid availability attribute");
876 bool EnvironmentMatchesOrNone
=
877 hasMatchingEnvironmentOrNone(SemaRef
.getASTContext(), AA
);
878 VersionTuple Introduced
= AA
->getIntroduced();
880 if (EnvironmentMatchesOrNone
&& AvailabilityStack
.back() >= Introduced
)
883 // If the context of this function is less available than D, we should not
884 // emit a diagnostic.
885 if (!ShouldDiagnoseAvailabilityInContext(SemaRef
, Result
, Introduced
,
886 AA
->getEnvironment(), Ctx
,
890 const TargetInfo
&TI
= SemaRef
.getASTContext().getTargetInfo();
891 std::string
PlatformName(
892 AvailabilityAttr::getPrettyPlatformName(TI
.getPlatformName()));
893 llvm::StringRef
TargetEnvironment(TI
.getTriple().getEnvironmentName());
894 llvm::StringRef AttrEnvironment
=
895 AA
->getEnvironment() ? AA
->getEnvironment()->getName() : "";
896 bool UseEnvironment
=
897 (!AttrEnvironment
.empty() && !TargetEnvironment
.empty());
899 unsigned DiagKind
= getAvailabilityDiagnosticKind(
901 SemaRef
.Context
.getTargetInfo().getPlatformMinVersion(), Introduced
,
902 EnvironmentMatchesOrNone
);
904 SemaRef
.Diag(Range
.getBegin(), DiagKind
)
905 << Range
<< D
<< PlatformName
<< Introduced
.getAsString()
906 << UseEnvironment
<< TargetEnvironment
;
908 SemaRef
.Diag(OffendingDecl
->getLocation(),
909 diag::note_partial_availability_specified_here
)
910 << OffendingDecl
<< PlatformName
<< Introduced
.getAsString()
911 << SemaRef
.Context
.getTargetInfo().getPlatformMinVersion().getAsString()
912 << UseEnvironment
<< AttrEnvironment
<< TargetEnvironment
;
914 // Do not offer to silence the warning or fixits for HLSL
915 if (SemaRef
.getLangOpts().HLSL
)
919 SemaRef
.Diag(Range
.getBegin(), diag::note_unguarded_available_silence
)
921 << (SemaRef
.getLangOpts().ObjC
? /*@available*/ 0
922 : /*__builtin_available*/ 1);
924 // Find the statement which should be enclosed in the if @available check.
925 if (StmtStack
.empty())
927 const Stmt
*StmtOfUse
= StmtStack
.back();
928 const CompoundStmt
*Scope
= nullptr;
929 for (const Stmt
*S
: llvm::reverse(StmtStack
)) {
930 if (const auto *CS
= dyn_cast
<CompoundStmt
>(S
)) {
934 if (isBodyLikeChildStmt(StmtOfUse
, S
)) {
935 // The declaration won't be seen outside of the statement, so we don't
936 // have to wrap the uses of any declared variables in if (@available).
937 // Therefore we can avoid setting Scope here.
942 const Stmt
*LastStmtOfUse
= nullptr;
943 if (isa
<DeclStmt
>(StmtOfUse
) && Scope
) {
944 for (const Decl
*D
: cast
<DeclStmt
>(StmtOfUse
)->decls()) {
945 if (StmtUSEFinder::isContained(StmtStack
.back(), D
)) {
946 LastStmtOfUse
= LastDeclUSEFinder::findLastStmtThatUsesDecl(D
, Scope
);
952 const SourceManager
&SM
= SemaRef
.getSourceManager();
953 SourceLocation IfInsertionLoc
=
954 SM
.getExpansionLoc(StmtOfUse
->getBeginLoc());
955 SourceLocation StmtEndLoc
=
956 SM
.getExpansionRange(
957 (LastStmtOfUse
? LastStmtOfUse
: StmtOfUse
)->getEndLoc())
959 if (SM
.getFileID(IfInsertionLoc
) != SM
.getFileID(StmtEndLoc
))
962 StringRef Indentation
= Lexer::getIndentationForLine(IfInsertionLoc
, SM
);
963 const char *ExtraIndentation
= " ";
964 std::string FixItString
;
965 llvm::raw_string_ostream
FixItOS(FixItString
);
966 FixItOS
<< "if (" << (SemaRef
.getLangOpts().ObjC
? "@available"
967 : "__builtin_available")
969 << AvailabilityAttr::getPlatformNameSourceSpelling(
970 SemaRef
.getASTContext().getTargetInfo().getPlatformName())
971 << " " << Introduced
.getAsString() << ", *)) {\n"
972 << Indentation
<< ExtraIndentation
;
973 FixitDiag
<< FixItHint::CreateInsertion(IfInsertionLoc
, FixItOS
.str());
974 SourceLocation ElseInsertionLoc
= Lexer::findLocationAfterToken(
975 StmtEndLoc
, tok::semi
, SM
, SemaRef
.getLangOpts(),
976 /*SkipTrailingWhitespaceAndNewLine=*/false);
977 if (ElseInsertionLoc
.isInvalid())
979 Lexer::getLocForEndOfToken(StmtEndLoc
, 0, SM
, SemaRef
.getLangOpts());
980 FixItOS
.str().clear();
982 << Indentation
<< "} else {\n"
983 << Indentation
<< ExtraIndentation
984 << "// Fallback on earlier versions\n"
985 << Indentation
<< "}";
986 FixitDiag
<< FixItHint::CreateInsertion(ElseInsertionLoc
, FixItOS
.str());
990 bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty
) {
991 const Type
*TyPtr
= Ty
.getTypePtr();
992 SourceRange Range
{Ty
.getBeginLoc(), Ty
.getEndLoc()};
994 if (Range
.isInvalid())
997 if (const auto *TT
= dyn_cast
<TagType
>(TyPtr
)) {
998 TagDecl
*TD
= TT
->getDecl();
999 DiagnoseDeclAvailability(TD
, Range
);
1001 } else if (const auto *TD
= dyn_cast
<TypedefType
>(TyPtr
)) {
1002 TypedefNameDecl
*D
= TD
->getDecl();
1003 DiagnoseDeclAvailability(D
, Range
);
1005 } else if (const auto *ObjCO
= dyn_cast
<ObjCObjectType
>(TyPtr
)) {
1006 if (NamedDecl
*D
= ObjCO
->getInterface())
1007 DiagnoseDeclAvailability(D
, Range
);
1013 struct ExtractedAvailabilityExpr
{
1014 const ObjCAvailabilityCheckExpr
*E
= nullptr;
1015 bool isNegated
= false;
1018 ExtractedAvailabilityExpr
extractAvailabilityExpr(const Expr
*IfCond
) {
1019 const auto *E
= IfCond
;
1020 bool IsNegated
= false;
1022 E
= E
->IgnoreParens();
1023 if (const auto *AE
= dyn_cast
<ObjCAvailabilityCheckExpr
>(E
)) {
1024 return ExtractedAvailabilityExpr
{AE
, IsNegated
};
1027 const auto *UO
= dyn_cast
<UnaryOperator
>(E
);
1028 if (!UO
|| UO
->getOpcode() != UO_LNot
) {
1029 return ExtractedAvailabilityExpr
{};
1031 E
= UO
->getSubExpr();
1032 IsNegated
= !IsNegated
;
1036 bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt
*If
) {
1037 ExtractedAvailabilityExpr IfCond
= extractAvailabilityExpr(If
->getCond());
1039 // This isn't an availability checking 'if', we can just continue.
1040 return DynamicRecursiveASTVisitor::TraverseIfStmt(If
);
1043 VersionTuple CondVersion
= IfCond
.E
->getVersion();
1044 // If we're using the '*' case here or if this check is redundant, then we
1045 // use the enclosing version to check both branches.
1046 if (CondVersion
.empty() || CondVersion
<= AvailabilityStack
.back()) {
1047 return TraverseStmt(If
->getThen()) && TraverseStmt(If
->getElse());
1050 auto *Guarded
= If
->getThen();
1051 auto *Unguarded
= If
->getElse();
1052 if (IfCond
.isNegated
) {
1053 std::swap(Guarded
, Unguarded
);
1056 AvailabilityStack
.push_back(CondVersion
);
1057 bool ShouldContinue
= TraverseStmt(Guarded
);
1058 AvailabilityStack
.pop_back();
1060 return ShouldContinue
&& TraverseStmt(Unguarded
);
1063 } // end anonymous namespace
1065 void Sema::DiagnoseUnguardedAvailabilityViolations(Decl
*D
) {
1066 Stmt
*Body
= nullptr;
1068 if (auto *FD
= D
->getAsFunction()) {
1069 Body
= FD
->getBody();
1071 if (auto *CD
= dyn_cast
<CXXConstructorDecl
>(FD
))
1072 for (const CXXCtorInitializer
*CI
: CD
->inits())
1073 DiagnoseUnguardedAvailability(*this, D
).IssueDiagnostics(CI
->getInit());
1075 } else if (auto *MD
= dyn_cast
<ObjCMethodDecl
>(D
))
1076 Body
= MD
->getBody();
1077 else if (auto *BD
= dyn_cast
<BlockDecl
>(D
))
1078 Body
= BD
->getBody();
1080 assert(Body
&& "Need a body here!");
1082 DiagnoseUnguardedAvailability(*this, D
).IssueDiagnostics(Body
);
1085 FunctionScopeInfo
*Sema::getCurFunctionAvailabilityContext() {
1086 if (FunctionScopes
.empty())
1089 // Conservatively search the entire current function scope context for
1090 // availability violations. This ensures we always correctly analyze nested
1091 // classes, blocks, lambdas, etc. that may or may not be inside if(@available)
1092 // checks themselves.
1093 return FunctionScopes
.front();
1096 void Sema::DiagnoseAvailabilityOfDecl(NamedDecl
*D
,
1097 ArrayRef
<SourceLocation
> Locs
,
1098 const ObjCInterfaceDecl
*UnknownObjCClass
,
1099 bool ObjCPropertyAccess
,
1100 bool AvoidPartialAvailabilityChecks
,
1101 ObjCInterfaceDecl
*ClassReceiver
) {
1102 std::string Message
;
1103 AvailabilityResult Result
;
1104 const NamedDecl
* OffendingDecl
;
1105 // See if this declaration is unavailable, deprecated, or partial.
1106 std::tie(Result
, OffendingDecl
) =
1107 ShouldDiagnoseAvailabilityOfDecl(*this, D
, &Message
, ClassReceiver
);
1108 if (Result
== AR_Available
)
1111 if (Result
== AR_NotYetIntroduced
) {
1112 if (AvoidPartialAvailabilityChecks
)
1115 // We need to know the @available context in the current function to
1116 // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that
1117 // when we're done parsing the current function.
1118 if (FunctionScopeInfo
*Context
= getCurFunctionAvailabilityContext()) {
1119 Context
->HasPotentialAvailabilityViolations
= true;
1124 const ObjCPropertyDecl
*ObjCPDecl
= nullptr;
1125 if (const auto *MD
= dyn_cast
<ObjCMethodDecl
>(D
)) {
1126 if (const ObjCPropertyDecl
*PD
= MD
->findPropertyDecl()) {
1127 AvailabilityResult PDeclResult
= PD
->getAvailability(nullptr);
1128 if (PDeclResult
== Result
)
1133 EmitAvailabilityWarning(*this, Result
, D
, OffendingDecl
, Message
, Locs
,
1134 UnknownObjCClass
, ObjCPDecl
, ObjCPropertyAccess
);