1dd8e0a0aSReid Kleckner //===--- SemaAvailability.cpp - Availability attribute handling -----------===// 2dd8e0a0aSReid Kleckner // 3dd8e0a0aSReid Kleckner // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4dd8e0a0aSReid Kleckner // See https://llvm.org/LICENSE.txt for license information. 5dd8e0a0aSReid Kleckner // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6dd8e0a0aSReid Kleckner // 7dd8e0a0aSReid Kleckner //===----------------------------------------------------------------------===// 8dd8e0a0aSReid Kleckner // 9dd8e0a0aSReid Kleckner // This file processes the availability attribute. 10dd8e0a0aSReid Kleckner // 11dd8e0a0aSReid Kleckner //===----------------------------------------------------------------------===// 12dd8e0a0aSReid Kleckner 13dd8e0a0aSReid Kleckner #include "clang/AST/Attr.h" 14dd8e0a0aSReid Kleckner #include "clang/AST/Decl.h" 15670fa2bdSGábor Horváth #include "clang/AST/DeclTemplate.h" 16*dde802b1SSirraide #include "clang/AST/DynamicRecursiveASTVisitor.h" 17*dde802b1SSirraide #include "clang/AST/ExprObjC.h" 18*dde802b1SSirraide #include "clang/AST/StmtObjC.h" 19dd8e0a0aSReid Kleckner #include "clang/Basic/DiagnosticSema.h" 203f33c4c1SHelena Kotas #include "clang/Basic/IdentifierTable.h" 218890209eSHelena Kotas #include "clang/Basic/LangOptions.h" 22d7c5037eSReid Kleckner #include "clang/Basic/TargetInfo.h" 23dd8e0a0aSReid Kleckner #include "clang/Lex/Preprocessor.h" 24dd8e0a0aSReid Kleckner #include "clang/Sema/DelayedDiagnostic.h" 25dd8e0a0aSReid Kleckner #include "clang/Sema/ScopeInfo.h" 26dd8e0a0aSReid Kleckner #include "clang/Sema/Sema.h" 2731a203faSVlad Serebrennikov #include "clang/Sema/SemaObjC.h" 283f33c4c1SHelena Kotas #include "llvm/ADT/StringRef.h" 29a1580d7bSKazu Hirata #include <optional> 30dd8e0a0aSReid Kleckner 31dd8e0a0aSReid Kleckner using namespace clang; 32dd8e0a0aSReid Kleckner using namespace sema; 33dd8e0a0aSReid Kleckner 343f33c4c1SHelena Kotas static bool hasMatchingEnvironmentOrNone(const ASTContext &Context, 353f33c4c1SHelena Kotas const AvailabilityAttr *AA) { 363f33c4c1SHelena Kotas IdentifierInfo *IIEnvironment = AA->getEnvironment(); 373f33c4c1SHelena Kotas auto Environment = Context.getTargetInfo().getTriple().getEnvironment(); 383f33c4c1SHelena Kotas if (!IIEnvironment || Environment == llvm::Triple::UnknownEnvironment) 393f33c4c1SHelena Kotas return true; 403f33c4c1SHelena Kotas 413f33c4c1SHelena Kotas llvm::Triple::EnvironmentType ET = 423f33c4c1SHelena Kotas AvailabilityAttr::getEnvironmentType(IIEnvironment->getName()); 433f33c4c1SHelena Kotas return Environment == ET; 443f33c4c1SHelena Kotas } 453f33c4c1SHelena Kotas 46dd8e0a0aSReid Kleckner static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, 47dd8e0a0aSReid Kleckner const Decl *D) { 483f33c4c1SHelena Kotas AvailabilityAttr const *PartialMatch = nullptr; 49dd8e0a0aSReid Kleckner // Check each AvailabilityAttr to find the one for this platform. 503f33c4c1SHelena Kotas // For multiple attributes with the same platform try to find one for this 513f33c4c1SHelena Kotas // environment. 52670fa2bdSGábor Horváth // The attribute is always on the FunctionDecl, not on the 53670fa2bdSGábor Horváth // FunctionTemplateDecl. 54670fa2bdSGábor Horváth if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D)) 55670fa2bdSGábor Horváth D = FTD->getTemplatedDecl(); 56dd8e0a0aSReid Kleckner for (const auto *A : D->attrs()) { 57dd8e0a0aSReid Kleckner if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) { 58dd8e0a0aSReid Kleckner // FIXME: this is copied from CheckAvailability. We should try to 59dd8e0a0aSReid Kleckner // de-duplicate. 60dd8e0a0aSReid Kleckner 61dd8e0a0aSReid Kleckner // Check if this is an App Extension "platform", and if so chop off 62dd8e0a0aSReid Kleckner // the suffix for matching with the actual platform. 63dd8e0a0aSReid Kleckner StringRef ActualPlatform = Avail->getPlatform()->getName(); 64dd8e0a0aSReid Kleckner StringRef RealizedPlatform = ActualPlatform; 65dd8e0a0aSReid Kleckner if (Context.getLangOpts().AppExt) { 66dd8e0a0aSReid Kleckner size_t suffix = RealizedPlatform.rfind("_app_extension"); 67dd8e0a0aSReid Kleckner if (suffix != StringRef::npos) 68dd8e0a0aSReid Kleckner RealizedPlatform = RealizedPlatform.slice(0, suffix); 69dd8e0a0aSReid Kleckner } 70dd8e0a0aSReid Kleckner 71dd8e0a0aSReid Kleckner StringRef TargetPlatform = Context.getTargetInfo().getPlatformName(); 72dd8e0a0aSReid Kleckner 73dd8e0a0aSReid Kleckner // Match the platform name. 743f33c4c1SHelena Kotas if (RealizedPlatform == TargetPlatform) { 753f33c4c1SHelena Kotas // Find the best matching attribute for this environment 763f33c4c1SHelena Kotas if (hasMatchingEnvironmentOrNone(Context, Avail)) 77dd8e0a0aSReid Kleckner return Avail; 783f33c4c1SHelena Kotas PartialMatch = Avail; 79dd8e0a0aSReid Kleckner } 80dd8e0a0aSReid Kleckner } 813f33c4c1SHelena Kotas } 823f33c4c1SHelena Kotas return PartialMatch; 83dd8e0a0aSReid Kleckner } 84dd8e0a0aSReid Kleckner 85dd8e0a0aSReid Kleckner /// The diagnostic we should emit for \c D, and the declaration that 86dd8e0a0aSReid Kleckner /// originated it, or \c AR_Available. 87dd8e0a0aSReid Kleckner /// 88dd8e0a0aSReid Kleckner /// \param D The declaration to check. 89dd8e0a0aSReid Kleckner /// \param Message If non-null, this will be populated with the message from 90dd8e0a0aSReid Kleckner /// the availability attribute that is selected. 918f77dc45SKazuaki Ishizaki /// \param ClassReceiver If we're checking the method of a class message 92dd8e0a0aSReid Kleckner /// send, the class. Otherwise nullptr. 93dd8e0a0aSReid Kleckner static std::pair<AvailabilityResult, const NamedDecl *> 94dd8e0a0aSReid Kleckner ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D, 95dd8e0a0aSReid Kleckner std::string *Message, 96dd8e0a0aSReid Kleckner ObjCInterfaceDecl *ClassReceiver) { 97dd8e0a0aSReid Kleckner AvailabilityResult Result = D->getAvailability(Message); 98dd8e0a0aSReid Kleckner 99dd8e0a0aSReid Kleckner // For typedefs, if the typedef declaration appears available look 100dd8e0a0aSReid Kleckner // to the underlying type to see if it is more restrictive. 101dd8e0a0aSReid Kleckner while (const auto *TD = dyn_cast<TypedefNameDecl>(D)) { 102dd8e0a0aSReid Kleckner if (Result == AR_Available) { 103dd8e0a0aSReid Kleckner if (const auto *TT = TD->getUnderlyingType()->getAs<TagType>()) { 104dd8e0a0aSReid Kleckner D = TT->getDecl(); 105dd8e0a0aSReid Kleckner Result = D->getAvailability(Message); 106dd8e0a0aSReid Kleckner continue; 107dd8e0a0aSReid Kleckner } 108dd8e0a0aSReid Kleckner } 109dd8e0a0aSReid Kleckner break; 110dd8e0a0aSReid Kleckner } 111dd8e0a0aSReid Kleckner 112d8e0b0d6Spremanandrao // For alias templates, get the underlying declaration. 113d8e0b0d6Spremanandrao if (const auto *ADecl = dyn_cast<TypeAliasTemplateDecl>(D)) { 114d8e0b0d6Spremanandrao D = ADecl->getTemplatedDecl(); 115d8e0b0d6Spremanandrao Result = D->getAvailability(Message); 116d8e0b0d6Spremanandrao } 117d8e0b0d6Spremanandrao 118dd8e0a0aSReid Kleckner // Forward class declarations get their attributes from their definition. 119dd8e0a0aSReid Kleckner if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) { 120dd8e0a0aSReid Kleckner if (IDecl->getDefinition()) { 121dd8e0a0aSReid Kleckner D = IDecl->getDefinition(); 122dd8e0a0aSReid Kleckner Result = D->getAvailability(Message); 123dd8e0a0aSReid Kleckner } 124dd8e0a0aSReid Kleckner } 125dd8e0a0aSReid Kleckner 126dd8e0a0aSReid Kleckner if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) 127dd8e0a0aSReid Kleckner if (Result == AR_Available) { 128dd8e0a0aSReid Kleckner const DeclContext *DC = ECD->getDeclContext(); 129dd8e0a0aSReid Kleckner if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) { 130dd8e0a0aSReid Kleckner Result = TheEnumDecl->getAvailability(Message); 131dd8e0a0aSReid Kleckner D = TheEnumDecl; 132dd8e0a0aSReid Kleckner } 133dd8e0a0aSReid Kleckner } 134dd8e0a0aSReid Kleckner 135dd8e0a0aSReid Kleckner // For +new, infer availability from -init. 136dd8e0a0aSReid Kleckner if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { 13731a203faSVlad Serebrennikov if (S.ObjC().NSAPIObj && ClassReceiver) { 138dd8e0a0aSReid Kleckner ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod( 13931a203faSVlad Serebrennikov S.ObjC().NSAPIObj->getInitSelector()); 140dd8e0a0aSReid Kleckner if (Init && Result == AR_Available && MD->isClassMethod() && 14131a203faSVlad Serebrennikov MD->getSelector() == S.ObjC().NSAPIObj->getNewSelector() && 142dd8e0a0aSReid Kleckner MD->definedInNSObject(S.getASTContext())) { 143dd8e0a0aSReid Kleckner Result = Init->getAvailability(Message); 144dd8e0a0aSReid Kleckner D = Init; 145dd8e0a0aSReid Kleckner } 146dd8e0a0aSReid Kleckner } 147dd8e0a0aSReid Kleckner } 148dd8e0a0aSReid Kleckner 149dd8e0a0aSReid Kleckner return {Result, D}; 150dd8e0a0aSReid Kleckner } 151dd8e0a0aSReid Kleckner 152dd8e0a0aSReid Kleckner 153dd8e0a0aSReid Kleckner /// whether we should emit a diagnostic for \c K and \c DeclVersion in 154dd8e0a0aSReid Kleckner /// the context of \c Ctx. For example, we should emit an unavailable diagnostic 155dd8e0a0aSReid Kleckner /// in a deprecated context, but not the other way around. 1563f33c4c1SHelena Kotas static bool ShouldDiagnoseAvailabilityInContext( 1573f33c4c1SHelena Kotas Sema &S, AvailabilityResult K, VersionTuple DeclVersion, 1583f33c4c1SHelena Kotas const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) { 159dd8e0a0aSReid Kleckner assert(K != AR_Available && "Expected an unavailable declaration here!"); 160dd8e0a0aSReid Kleckner 161bb58748eSzoecarver // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic. 162bb58748eSzoecarver auto DeclLoc = Ctx->getBeginLoc(); 163bb58748eSzoecarver // This is only a problem in Foundation's C++ implementation for CF_OPTIONS. 164bb58748eSzoecarver if (DeclLoc.isMacroID() && S.getLangOpts().CPlusPlus && 165bb58748eSzoecarver isa<TypedefDecl>(OffendingDecl)) { 166bb58748eSzoecarver StringRef MacroName = S.getPreprocessor().getImmediateMacroName(DeclLoc); 167bb58748eSzoecarver if (MacroName == "CF_OPTIONS" || MacroName == "OBJC_OPTIONS" || 168bb58748eSzoecarver MacroName == "SWIFT_OPTIONS" || MacroName == "NS_OPTIONS") { 169bb58748eSzoecarver return false; 170bb58748eSzoecarver } 171bb58748eSzoecarver } 172bb58748eSzoecarver 17330efdce7SHelena Kotas // In HLSL, skip emitting diagnostic if the diagnostic mode is not set to 17430efdce7SHelena Kotas // strict (-fhlsl-strict-availability), or if the target is library and the 17530efdce7SHelena Kotas // availability is restricted to a specific environment/shader stage. 17630efdce7SHelena Kotas // For libraries the availability will be checked later in 17730efdce7SHelena Kotas // DiagnoseHLSLAvailability class once where the specific environment/shader 17830efdce7SHelena Kotas // stage of the caller is known. 17930efdce7SHelena Kotas if (S.getLangOpts().HLSL) { 18030efdce7SHelena Kotas if (!S.getLangOpts().HLSLStrictAvailability || 18130efdce7SHelena Kotas (DeclEnv != nullptr && 18230efdce7SHelena Kotas S.getASTContext().getTargetInfo().getTriple().getEnvironment() == 18330efdce7SHelena Kotas llvm::Triple::EnvironmentType::Library)) 18430efdce7SHelena Kotas return false; 18530efdce7SHelena Kotas } 18630efdce7SHelena Kotas 187b89f09f5SOleksandr T. if (K == AR_Deprecated) { 188b89f09f5SOleksandr T. if (const auto *VD = dyn_cast<VarDecl>(OffendingDecl)) 189b89f09f5SOleksandr T. if (VD->isLocalVarDeclOrParm() && VD->isDeprecated()) 190b89f09f5SOleksandr T. return true; 191b89f09f5SOleksandr T. } 192b89f09f5SOleksandr T. 193dd8e0a0aSReid Kleckner // Checks if we should emit the availability diagnostic in the context of C. 194dd8e0a0aSReid Kleckner auto CheckContext = [&](const Decl *C) { 195dd8e0a0aSReid Kleckner if (K == AR_NotYetIntroduced) { 196dd8e0a0aSReid Kleckner if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C)) 1973f33c4c1SHelena Kotas if (AA->getIntroduced() >= DeclVersion && 1983f33c4c1SHelena Kotas AA->getEnvironment() == DeclEnv) 199dd8e0a0aSReid Kleckner return true; 200dd8e0a0aSReid Kleckner } else if (K == AR_Deprecated) { 201dd8e0a0aSReid Kleckner if (C->isDeprecated()) 202dd8e0a0aSReid Kleckner return true; 203dd8e0a0aSReid Kleckner } else if (K == AR_Unavailable) { 204dd8e0a0aSReid Kleckner // It is perfectly fine to refer to an 'unavailable' Objective-C method 205dd8e0a0aSReid Kleckner // when it is referenced from within the @implementation itself. In this 206dd8e0a0aSReid Kleckner // context, we interpret unavailable as a form of access control. 207dd8e0a0aSReid Kleckner if (const auto *MD = dyn_cast<ObjCMethodDecl>(OffendingDecl)) { 208dd8e0a0aSReid Kleckner if (const auto *Impl = dyn_cast<ObjCImplDecl>(C)) { 209dd8e0a0aSReid Kleckner if (MD->getClassInterface() == Impl->getClassInterface()) 210dd8e0a0aSReid Kleckner return true; 211dd8e0a0aSReid Kleckner } 212dd8e0a0aSReid Kleckner } 213dd8e0a0aSReid Kleckner } 214dd8e0a0aSReid Kleckner 215dd8e0a0aSReid Kleckner if (C->isUnavailable()) 216dd8e0a0aSReid Kleckner return true; 217dd8e0a0aSReid Kleckner return false; 218dd8e0a0aSReid Kleckner }; 219dd8e0a0aSReid Kleckner 220dd8e0a0aSReid Kleckner do { 221dd8e0a0aSReid Kleckner if (CheckContext(Ctx)) 222dd8e0a0aSReid Kleckner return false; 223dd8e0a0aSReid Kleckner 224dd8e0a0aSReid Kleckner // An implementation implicitly has the availability of the interface. 225dd8e0a0aSReid Kleckner // Unless it is "+load" method. 226dd8e0a0aSReid Kleckner if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Ctx)) 227dd8e0a0aSReid Kleckner if (MethodD->isClassMethod() && 228dd8e0a0aSReid Kleckner MethodD->getSelector().getAsString() == "load") 229dd8e0a0aSReid Kleckner return true; 230dd8e0a0aSReid Kleckner 231dd8e0a0aSReid Kleckner if (const auto *CatOrImpl = dyn_cast<ObjCImplDecl>(Ctx)) { 232dd8e0a0aSReid Kleckner if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface()) 233dd8e0a0aSReid Kleckner if (CheckContext(Interface)) 234dd8e0a0aSReid Kleckner return false; 235dd8e0a0aSReid Kleckner } 236dd8e0a0aSReid Kleckner // A category implicitly has the availability of the interface. 237dd8e0a0aSReid Kleckner else if (const auto *CatD = dyn_cast<ObjCCategoryDecl>(Ctx)) 238dd8e0a0aSReid Kleckner if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface()) 239dd8e0a0aSReid Kleckner if (CheckContext(Interface)) 240dd8e0a0aSReid Kleckner return false; 241dd8e0a0aSReid Kleckner } while ((Ctx = cast_or_null<Decl>(Ctx->getDeclContext()))); 242dd8e0a0aSReid Kleckner 243dd8e0a0aSReid Kleckner return true; 244dd8e0a0aSReid Kleckner } 245dd8e0a0aSReid Kleckner 24630efdce7SHelena Kotas static unsigned getAvailabilityDiagnosticKind( 24730efdce7SHelena Kotas const ASTContext &Context, const VersionTuple &DeploymentVersion, 24830efdce7SHelena Kotas const VersionTuple &DeclVersion, bool HasMatchingEnv) { 249dd8e0a0aSReid Kleckner const auto &Triple = Context.getTargetInfo().getTriple(); 250dd8e0a0aSReid Kleckner VersionTuple ForceAvailabilityFromVersion; 251dd8e0a0aSReid Kleckner switch (Triple.getOS()) { 25230efdce7SHelena Kotas // For iOS, emit the diagnostic even if -Wunguarded-availability is 25330efdce7SHelena Kotas // not specified for deployment targets >= to iOS 11 or equivalent or 25430efdce7SHelena Kotas // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or 25530efdce7SHelena Kotas // later. 256dd8e0a0aSReid Kleckner case llvm::Triple::IOS: 257dd8e0a0aSReid Kleckner case llvm::Triple::TvOS: 258dd8e0a0aSReid Kleckner ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11); 259dd8e0a0aSReid Kleckner break; 260dd8e0a0aSReid Kleckner case llvm::Triple::WatchOS: 261dd8e0a0aSReid Kleckner ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4); 262dd8e0a0aSReid Kleckner break; 263dd8e0a0aSReid Kleckner case llvm::Triple::Darwin: 264dd8e0a0aSReid Kleckner case llvm::Triple::MacOSX: 265dd8e0a0aSReid Kleckner ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13); 266dd8e0a0aSReid Kleckner break; 26730efdce7SHelena Kotas // For HLSL, use diagnostic from HLSLAvailability group which 26830efdce7SHelena Kotas // are reported as errors by default and in strict diagnostic mode 26930efdce7SHelena Kotas // (-fhlsl-strict-availability) and as warnings in relaxed diagnostic 27030efdce7SHelena Kotas // mode (-Wno-error=hlsl-availability) 27110378c45SChris Bieneman case llvm::Triple::ShaderModel: 27230efdce7SHelena Kotas return HasMatchingEnv ? diag::warn_hlsl_availability 27330efdce7SHelena Kotas : diag::warn_hlsl_availability_unavailable; 274dd8e0a0aSReid Kleckner default: 27530efdce7SHelena Kotas // New Apple targets should always warn about availability. 27630efdce7SHelena Kotas ForceAvailabilityFromVersion = 27730efdce7SHelena Kotas (Triple.getVendor() == llvm::Triple::Apple) 27830efdce7SHelena Kotas ? VersionTuple(/*Major=*/0, 0) 27930efdce7SHelena Kotas : VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1); 280dd8e0a0aSReid Kleckner } 28130efdce7SHelena Kotas if (DeploymentVersion >= ForceAvailabilityFromVersion || 28230efdce7SHelena Kotas DeclVersion >= ForceAvailabilityFromVersion) 28330efdce7SHelena Kotas return HasMatchingEnv ? diag::warn_unguarded_availability_new 28430efdce7SHelena Kotas : diag::warn_unguarded_availability_unavailable_new; 28530efdce7SHelena Kotas return HasMatchingEnv ? diag::warn_unguarded_availability 28630efdce7SHelena Kotas : diag::warn_unguarded_availability_unavailable; 287dd8e0a0aSReid Kleckner } 288dd8e0a0aSReid Kleckner 289dd8e0a0aSReid Kleckner static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) { 290dd8e0a0aSReid Kleckner for (Decl *Ctx = OrigCtx; Ctx; 291dd8e0a0aSReid Kleckner Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) { 292dd8e0a0aSReid Kleckner if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx)) 293dd8e0a0aSReid Kleckner return cast<NamedDecl>(Ctx); 294dd8e0a0aSReid Kleckner if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) { 295dd8e0a0aSReid Kleckner if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx)) 296dd8e0a0aSReid Kleckner return Imp->getClassInterface(); 297dd8e0a0aSReid Kleckner return CD; 298dd8e0a0aSReid Kleckner } 299dd8e0a0aSReid Kleckner } 300dd8e0a0aSReid Kleckner 301dd8e0a0aSReid Kleckner return dyn_cast<NamedDecl>(OrigCtx); 302dd8e0a0aSReid Kleckner } 303dd8e0a0aSReid Kleckner 304dd8e0a0aSReid Kleckner namespace { 305dd8e0a0aSReid Kleckner 306dd8e0a0aSReid Kleckner struct AttributeInsertion { 307dd8e0a0aSReid Kleckner StringRef Prefix; 308dd8e0a0aSReid Kleckner SourceLocation Loc; 309dd8e0a0aSReid Kleckner StringRef Suffix; 310dd8e0a0aSReid Kleckner 311dd8e0a0aSReid Kleckner static AttributeInsertion createInsertionAfter(const NamedDecl *D) { 312dd8e0a0aSReid Kleckner return {" ", D->getEndLoc(), ""}; 313dd8e0a0aSReid Kleckner } 314dd8e0a0aSReid Kleckner static AttributeInsertion createInsertionAfter(SourceLocation Loc) { 315dd8e0a0aSReid Kleckner return {" ", Loc, ""}; 316dd8e0a0aSReid Kleckner } 317dd8e0a0aSReid Kleckner static AttributeInsertion createInsertionBefore(const NamedDecl *D) { 318dd8e0a0aSReid Kleckner return {"", D->getBeginLoc(), "\n"}; 319dd8e0a0aSReid Kleckner } 320dd8e0a0aSReid Kleckner }; 321dd8e0a0aSReid Kleckner 322dd8e0a0aSReid Kleckner } // end anonymous namespace 323dd8e0a0aSReid Kleckner 324dd8e0a0aSReid Kleckner /// Tries to parse a string as ObjC method name. 325dd8e0a0aSReid Kleckner /// 326dd8e0a0aSReid Kleckner /// \param Name The string to parse. Expected to originate from availability 327dd8e0a0aSReid Kleckner /// attribute argument. 328dd8e0a0aSReid Kleckner /// \param SlotNames The vector that will be populated with slot names. In case 329dd8e0a0aSReid Kleckner /// of unsuccessful parsing can contain invalid data. 33037a3e98cSKazu Hirata /// \returns A number of method parameters if parsing was successful, 33137a3e98cSKazu Hirata /// std::nullopt otherwise. 3326ad0788cSKazu Hirata static std::optional<unsigned> 333dd8e0a0aSReid Kleckner tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &SlotNames, 334dd8e0a0aSReid Kleckner const LangOptions &LangOpts) { 335dd8e0a0aSReid Kleckner // Accept replacements starting with - or + as valid ObjC method names. 336dd8e0a0aSReid Kleckner if (!Name.empty() && (Name.front() == '-' || Name.front() == '+')) 337dd8e0a0aSReid Kleckner Name = Name.drop_front(1); 338dd8e0a0aSReid Kleckner if (Name.empty()) 3398595f2e5SKazu Hirata return std::nullopt; 340dd8e0a0aSReid Kleckner Name.split(SlotNames, ':'); 341dd8e0a0aSReid Kleckner unsigned NumParams; 342dd8e0a0aSReid Kleckner if (Name.back() == ':') { 343dd8e0a0aSReid Kleckner // Remove an empty string at the end that doesn't represent any slot. 344dd8e0a0aSReid Kleckner SlotNames.pop_back(); 345dd8e0a0aSReid Kleckner NumParams = SlotNames.size(); 346dd8e0a0aSReid Kleckner } else { 347dd8e0a0aSReid Kleckner if (SlotNames.size() != 1) 348dd8e0a0aSReid Kleckner // Not a valid method name, just a colon-separated string. 3498595f2e5SKazu Hirata return std::nullopt; 350dd8e0a0aSReid Kleckner NumParams = 0; 351dd8e0a0aSReid Kleckner } 352dd8e0a0aSReid Kleckner // Verify all slot names are valid. 353dd8e0a0aSReid Kleckner bool AllowDollar = LangOpts.DollarIdents; 354dd8e0a0aSReid Kleckner for (StringRef S : SlotNames) { 355dd8e0a0aSReid Kleckner if (S.empty()) 356dd8e0a0aSReid Kleckner continue; 357601102d2SCorentin Jabot if (!isValidAsciiIdentifier(S, AllowDollar)) 3588595f2e5SKazu Hirata return std::nullopt; 359dd8e0a0aSReid Kleckner } 360dd8e0a0aSReid Kleckner return NumParams; 361dd8e0a0aSReid Kleckner } 362dd8e0a0aSReid Kleckner 363dd8e0a0aSReid Kleckner /// Returns a source location in which it's appropriate to insert a new 364dd8e0a0aSReid Kleckner /// attribute for the given declaration \D. 3656ad0788cSKazu Hirata static std::optional<AttributeInsertion> 366dd8e0a0aSReid Kleckner createAttributeInsertion(const NamedDecl *D, const SourceManager &SM, 367dd8e0a0aSReid Kleckner const LangOptions &LangOpts) { 368dd8e0a0aSReid Kleckner if (isa<ObjCPropertyDecl>(D)) 369dd8e0a0aSReid Kleckner return AttributeInsertion::createInsertionAfter(D); 370dd8e0a0aSReid Kleckner if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { 371dd8e0a0aSReid Kleckner if (MD->hasBody()) 3728595f2e5SKazu Hirata return std::nullopt; 373dd8e0a0aSReid Kleckner return AttributeInsertion::createInsertionAfter(D); 374dd8e0a0aSReid Kleckner } 375dd8e0a0aSReid Kleckner if (const auto *TD = dyn_cast<TagDecl>(D)) { 376dd8e0a0aSReid Kleckner SourceLocation Loc = 377dd8e0a0aSReid Kleckner Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 0, SM, LangOpts); 378dd8e0a0aSReid Kleckner if (Loc.isInvalid()) 3798595f2e5SKazu Hirata return std::nullopt; 380dd8e0a0aSReid Kleckner // Insert after the 'struct'/whatever keyword. 381dd8e0a0aSReid Kleckner return AttributeInsertion::createInsertionAfter(Loc); 382dd8e0a0aSReid Kleckner } 383dd8e0a0aSReid Kleckner return AttributeInsertion::createInsertionBefore(D); 384dd8e0a0aSReid Kleckner } 385dd8e0a0aSReid Kleckner 386dd8e0a0aSReid Kleckner /// Actually emit an availability diagnostic for a reference to an unavailable 387dd8e0a0aSReid Kleckner /// decl. 388dd8e0a0aSReid Kleckner /// 389dd8e0a0aSReid Kleckner /// \param Ctx The context that the reference occurred in 390dd8e0a0aSReid Kleckner /// \param ReferringDecl The exact declaration that was referenced. 391dd8e0a0aSReid Kleckner /// \param OffendingDecl A related decl to \c ReferringDecl that has an 392dd8e0a0aSReid Kleckner /// availability attribute corresponding to \c K attached to it. Note that this 393dd8e0a0aSReid Kleckner /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and 394dd8e0a0aSReid Kleckner /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl 395dd8e0a0aSReid Kleckner /// and OffendingDecl is the EnumDecl. 396dd8e0a0aSReid Kleckner static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, 397dd8e0a0aSReid Kleckner Decl *Ctx, const NamedDecl *ReferringDecl, 398dd8e0a0aSReid Kleckner const NamedDecl *OffendingDecl, 399dd8e0a0aSReid Kleckner StringRef Message, 400dd8e0a0aSReid Kleckner ArrayRef<SourceLocation> Locs, 401dd8e0a0aSReid Kleckner const ObjCInterfaceDecl *UnknownObjCClass, 402dd8e0a0aSReid Kleckner const ObjCPropertyDecl *ObjCProperty, 403dd8e0a0aSReid Kleckner bool ObjCPropertyAccess) { 404dd8e0a0aSReid Kleckner // Diagnostics for deprecated or unavailable. 405dd8e0a0aSReid Kleckner unsigned diag, diag_message, diag_fwdclass_message; 406dd8e0a0aSReid Kleckner unsigned diag_available_here = diag::note_availability_specified_here; 407dd8e0a0aSReid Kleckner SourceLocation NoteLocation = OffendingDecl->getLocation(); 408dd8e0a0aSReid Kleckner 409dd8e0a0aSReid Kleckner // Matches 'diag::note_property_attribute' options. 410dd8e0a0aSReid Kleckner unsigned property_note_select; 411dd8e0a0aSReid Kleckner 412dd8e0a0aSReid Kleckner // Matches diag::note_availability_specified_here. 413dd8e0a0aSReid Kleckner unsigned available_here_select_kind; 414dd8e0a0aSReid Kleckner 415dd8e0a0aSReid Kleckner VersionTuple DeclVersion; 4163f33c4c1SHelena Kotas const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl); 4173f33c4c1SHelena Kotas const IdentifierInfo *IIEnv = nullptr; 4183f33c4c1SHelena Kotas if (AA) { 419dd8e0a0aSReid Kleckner DeclVersion = AA->getIntroduced(); 4203f33c4c1SHelena Kotas IIEnv = AA->getEnvironment(); 4213f33c4c1SHelena Kotas } 422dd8e0a0aSReid Kleckner 4233f33c4c1SHelena Kotas if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, IIEnv, Ctx, 424dd8e0a0aSReid Kleckner OffendingDecl)) 425dd8e0a0aSReid Kleckner return; 426dd8e0a0aSReid Kleckner 427dd8e0a0aSReid Kleckner SourceLocation Loc = Locs.front(); 428dd8e0a0aSReid Kleckner 429dd8e0a0aSReid Kleckner // The declaration can have multiple availability attributes, we are looking 430dd8e0a0aSReid Kleckner // at one of them. 4313f33c4c1SHelena Kotas if (AA && AA->isInherited()) { 432dd8e0a0aSReid Kleckner for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl; 433dd8e0a0aSReid Kleckner Redecl = Redecl->getPreviousDecl()) { 434dd8e0a0aSReid Kleckner const AvailabilityAttr *AForRedecl = 435dd8e0a0aSReid Kleckner getAttrForPlatform(S.Context, Redecl); 436dd8e0a0aSReid Kleckner if (AForRedecl && !AForRedecl->isInherited()) { 437dd8e0a0aSReid Kleckner // If D is a declaration with inherited attributes, the note should 438dd8e0a0aSReid Kleckner // point to the declaration with actual attributes. 439dd8e0a0aSReid Kleckner NoteLocation = Redecl->getLocation(); 440dd8e0a0aSReid Kleckner break; 441dd8e0a0aSReid Kleckner } 442dd8e0a0aSReid Kleckner } 443dd8e0a0aSReid Kleckner } 444dd8e0a0aSReid Kleckner 445dd8e0a0aSReid Kleckner switch (K) { 446dd8e0a0aSReid Kleckner case AR_NotYetIntroduced: { 447dd8e0a0aSReid Kleckner // We would like to emit the diagnostic even if -Wunguarded-availability is 448dd8e0a0aSReid Kleckner // not specified for deployment targets >= to iOS 11 or equivalent or 449dd8e0a0aSReid Kleckner // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or 450dd8e0a0aSReid Kleckner // later. 4513f33c4c1SHelena Kotas assert(AA != nullptr && "expecting valid availability attribute"); 452dd8e0a0aSReid Kleckner VersionTuple Introduced = AA->getIntroduced(); 4533f33c4c1SHelena Kotas bool EnvironmentMatchesOrNone = 4543f33c4c1SHelena Kotas hasMatchingEnvironmentOrNone(S.getASTContext(), AA); 4553f33c4c1SHelena Kotas 4563f33c4c1SHelena Kotas const TargetInfo &TI = S.getASTContext().getTargetInfo(); 4573f33c4c1SHelena Kotas std::string PlatformName( 4583f33c4c1SHelena Kotas AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); 45930efdce7SHelena Kotas llvm::StringRef TargetEnvironment( 46030efdce7SHelena Kotas llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment())); 4613f33c4c1SHelena Kotas llvm::StringRef AttrEnvironment = 46230efdce7SHelena Kotas AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; 4633f33c4c1SHelena Kotas bool UseEnvironment = 4643f33c4c1SHelena Kotas (!AttrEnvironment.empty() && !TargetEnvironment.empty()); 465dd8e0a0aSReid Kleckner 46630efdce7SHelena Kotas unsigned DiagKind = getAvailabilityDiagnosticKind( 467dd8e0a0aSReid Kleckner S.Context, S.Context.getTargetInfo().getPlatformMinVersion(), 46830efdce7SHelena Kotas Introduced, EnvironmentMatchesOrNone); 469dd8e0a0aSReid Kleckner 4703f33c4c1SHelena Kotas S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName 4713f33c4c1SHelena Kotas << Introduced.getAsString() << UseEnvironment 4723f33c4c1SHelena Kotas << TargetEnvironment; 473dd8e0a0aSReid Kleckner 474dd8e0a0aSReid Kleckner S.Diag(OffendingDecl->getLocation(), 475dd8e0a0aSReid Kleckner diag::note_partial_availability_specified_here) 476dd8e0a0aSReid Kleckner << OffendingDecl << PlatformName << Introduced.getAsString() 4773f33c4c1SHelena Kotas << S.Context.getTargetInfo().getPlatformMinVersion().getAsString() 4783f33c4c1SHelena Kotas << UseEnvironment << AttrEnvironment << TargetEnvironment; 479dd8e0a0aSReid Kleckner 4808890209eSHelena Kotas // Do not offer to silence the warning or fixits for HLSL 4818890209eSHelena Kotas if (S.getLangOpts().HLSL) 4828890209eSHelena Kotas return; 4838890209eSHelena Kotas 484dd8e0a0aSReid Kleckner if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) { 485dd8e0a0aSReid Kleckner if (const auto *TD = dyn_cast<TagDecl>(Enclosing)) 486dd8e0a0aSReid Kleckner if (TD->getDeclName().isEmpty()) { 487dd8e0a0aSReid Kleckner S.Diag(TD->getLocation(), 488dd8e0a0aSReid Kleckner diag::note_decl_unguarded_availability_silence) 489dd8e0a0aSReid Kleckner << /*Anonymous*/ 1 << TD->getKindName(); 490dd8e0a0aSReid Kleckner return; 491dd8e0a0aSReid Kleckner } 492dd8e0a0aSReid Kleckner auto FixitNoteDiag = 493dd8e0a0aSReid Kleckner S.Diag(Enclosing->getLocation(), 494dd8e0a0aSReid Kleckner diag::note_decl_unguarded_availability_silence) 495dd8e0a0aSReid Kleckner << /*Named*/ 0 << Enclosing; 496dd8e0a0aSReid Kleckner // Don't offer a fixit for declarations with availability attributes. 497dd8e0a0aSReid Kleckner if (Enclosing->hasAttr<AvailabilityAttr>()) 498dd8e0a0aSReid Kleckner return; 499319e8cd2SIan Anderson Preprocessor &PP = S.getPreprocessor(); 500319e8cd2SIan Anderson if (!PP.isMacroDefined("API_AVAILABLE")) 501dd8e0a0aSReid Kleckner return; 5026ad0788cSKazu Hirata std::optional<AttributeInsertion> Insertion = createAttributeInsertion( 503dd8e0a0aSReid Kleckner Enclosing, S.getSourceManager(), S.getLangOpts()); 504dd8e0a0aSReid Kleckner if (!Insertion) 505dd8e0a0aSReid Kleckner return; 506319e8cd2SIan Anderson StringRef PlatformName = 507319e8cd2SIan Anderson S.getASTContext().getTargetInfo().getPlatformName(); 508319e8cd2SIan Anderson 509319e8cd2SIan Anderson // Apple's API_AVAILABLE macro expands roughly like this. 510319e8cd2SIan Anderson // API_AVAILABLE(ios(17.0)) 511319e8cd2SIan Anderson // __attribute__((availability(__API_AVAILABLE_PLATFORM_ios(17.0))) 512319e8cd2SIan Anderson // __attribute__((availability(ios,introduced=17.0))) 513319e8cd2SIan Anderson // In order to figure out which platform name to use in the API_AVAILABLE 514319e8cd2SIan Anderson // macro, the associated __API_AVAILABLE_PLATFORM_ macro needs to be 515319e8cd2SIan Anderson // found. The __API_AVAILABLE_PLATFORM_ macros aren't consistent about 516319e8cd2SIan Anderson // using the canonical platform name, source spelling name, or one of the 517319e8cd2SIan Anderson // other supported names (i.e. one of the keys in canonicalizePlatformName 518319e8cd2SIan Anderson // that's neither). Check all of the supported names for a match. 519319e8cd2SIan Anderson std::vector<StringRef> EquivalentPlatforms = 520319e8cd2SIan Anderson AvailabilityAttr::equivalentPlatformNames(PlatformName); 521319e8cd2SIan Anderson llvm::Twine MacroPrefix = "__API_AVAILABLE_PLATFORM_"; 522319e8cd2SIan Anderson auto AvailablePlatform = 523319e8cd2SIan Anderson llvm::find_if(EquivalentPlatforms, [&](StringRef EquivalentPlatform) { 524319e8cd2SIan Anderson return PP.isMacroDefined((MacroPrefix + EquivalentPlatform).str()); 525319e8cd2SIan Anderson }); 526319e8cd2SIan Anderson if (AvailablePlatform == EquivalentPlatforms.end()) 527319e8cd2SIan Anderson return; 528dd8e0a0aSReid Kleckner std::string Introduced = 529dd8e0a0aSReid Kleckner OffendingDecl->getVersionIntroduced().getAsString(); 530dd8e0a0aSReid Kleckner FixitNoteDiag << FixItHint::CreateInsertion( 531dd8e0a0aSReid Kleckner Insertion->Loc, 532319e8cd2SIan Anderson (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" + 533319e8cd2SIan Anderson *AvailablePlatform + "(" + Introduced + "))" + Insertion->Suffix) 534dd8e0a0aSReid Kleckner .str()); 535dd8e0a0aSReid Kleckner } 536dd8e0a0aSReid Kleckner return; 537dd8e0a0aSReid Kleckner } 538dd8e0a0aSReid Kleckner case AR_Deprecated: 539dd8e0a0aSReid Kleckner diag = !ObjCPropertyAccess ? diag::warn_deprecated 540dd8e0a0aSReid Kleckner : diag::warn_property_method_deprecated; 541dd8e0a0aSReid Kleckner diag_message = diag::warn_deprecated_message; 542dd8e0a0aSReid Kleckner diag_fwdclass_message = diag::warn_deprecated_fwdclass_message; 543dd8e0a0aSReid Kleckner property_note_select = /* deprecated */ 0; 544dd8e0a0aSReid Kleckner available_here_select_kind = /* deprecated */ 2; 545dd8e0a0aSReid Kleckner if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>()) 546dd8e0a0aSReid Kleckner NoteLocation = AL->getLocation(); 547dd8e0a0aSReid Kleckner break; 548dd8e0a0aSReid Kleckner 549dd8e0a0aSReid Kleckner case AR_Unavailable: 550dd8e0a0aSReid Kleckner diag = !ObjCPropertyAccess ? diag::err_unavailable 551dd8e0a0aSReid Kleckner : diag::err_property_method_unavailable; 552dd8e0a0aSReid Kleckner diag_message = diag::err_unavailable_message; 553dd8e0a0aSReid Kleckner diag_fwdclass_message = diag::warn_unavailable_fwdclass_message; 554dd8e0a0aSReid Kleckner property_note_select = /* unavailable */ 1; 555dd8e0a0aSReid Kleckner available_here_select_kind = /* unavailable */ 0; 556dd8e0a0aSReid Kleckner 557dd8e0a0aSReid Kleckner if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) { 558dd8e0a0aSReid Kleckner if (AL->isImplicit() && AL->getImplicitReason()) { 559dd8e0a0aSReid Kleckner // Most of these failures are due to extra restrictions in ARC; 560dd8e0a0aSReid Kleckner // reflect that in the primary diagnostic when applicable. 561dd8e0a0aSReid Kleckner auto flagARCError = [&] { 562dd8e0a0aSReid Kleckner if (S.getLangOpts().ObjCAutoRefCount && 563dd8e0a0aSReid Kleckner S.getSourceManager().isInSystemHeader( 564dd8e0a0aSReid Kleckner OffendingDecl->getLocation())) 565dd8e0a0aSReid Kleckner diag = diag::err_unavailable_in_arc; 566dd8e0a0aSReid Kleckner }; 567dd8e0a0aSReid Kleckner 568dd8e0a0aSReid Kleckner switch (AL->getImplicitReason()) { 569dd8e0a0aSReid Kleckner case UnavailableAttr::IR_None: break; 570dd8e0a0aSReid Kleckner 571dd8e0a0aSReid Kleckner case UnavailableAttr::IR_ARCForbiddenType: 572dd8e0a0aSReid Kleckner flagARCError(); 573dd8e0a0aSReid Kleckner diag_available_here = diag::note_arc_forbidden_type; 574dd8e0a0aSReid Kleckner break; 575dd8e0a0aSReid Kleckner 576dd8e0a0aSReid Kleckner case UnavailableAttr::IR_ForbiddenWeak: 577dd8e0a0aSReid Kleckner if (S.getLangOpts().ObjCWeakRuntime) 578dd8e0a0aSReid Kleckner diag_available_here = diag::note_arc_weak_disabled; 579dd8e0a0aSReid Kleckner else 580dd8e0a0aSReid Kleckner diag_available_here = diag::note_arc_weak_no_runtime; 581dd8e0a0aSReid Kleckner break; 582dd8e0a0aSReid Kleckner 583dd8e0a0aSReid Kleckner case UnavailableAttr::IR_ARCForbiddenConversion: 584dd8e0a0aSReid Kleckner flagARCError(); 585dd8e0a0aSReid Kleckner diag_available_here = diag::note_performs_forbidden_arc_conversion; 586dd8e0a0aSReid Kleckner break; 587dd8e0a0aSReid Kleckner 588dd8e0a0aSReid Kleckner case UnavailableAttr::IR_ARCInitReturnsUnrelated: 589dd8e0a0aSReid Kleckner flagARCError(); 590dd8e0a0aSReid Kleckner diag_available_here = diag::note_arc_init_returns_unrelated; 591dd8e0a0aSReid Kleckner break; 592dd8e0a0aSReid Kleckner 593dd8e0a0aSReid Kleckner case UnavailableAttr::IR_ARCFieldWithOwnership: 594dd8e0a0aSReid Kleckner flagARCError(); 595dd8e0a0aSReid Kleckner diag_available_here = diag::note_arc_field_with_ownership; 596dd8e0a0aSReid Kleckner break; 597dd8e0a0aSReid Kleckner } 598dd8e0a0aSReid Kleckner } 599dd8e0a0aSReid Kleckner } 600dd8e0a0aSReid Kleckner break; 601dd8e0a0aSReid Kleckner 602dd8e0a0aSReid Kleckner case AR_Available: 603dd8e0a0aSReid Kleckner llvm_unreachable("Warning for availability of available declaration?"); 604dd8e0a0aSReid Kleckner } 605dd8e0a0aSReid Kleckner 606dd8e0a0aSReid Kleckner SmallVector<FixItHint, 12> FixIts; 607dd8e0a0aSReid Kleckner if (K == AR_Deprecated) { 608dd8e0a0aSReid Kleckner StringRef Replacement; 609dd8e0a0aSReid Kleckner if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>()) 610dd8e0a0aSReid Kleckner Replacement = AL->getReplacement(); 611dd8e0a0aSReid Kleckner if (auto AL = getAttrForPlatform(S.Context, OffendingDecl)) 612dd8e0a0aSReid Kleckner Replacement = AL->getReplacement(); 613dd8e0a0aSReid Kleckner 614dd8e0a0aSReid Kleckner CharSourceRange UseRange; 615dd8e0a0aSReid Kleckner if (!Replacement.empty()) 616dd8e0a0aSReid Kleckner UseRange = 617dd8e0a0aSReid Kleckner CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); 618dd8e0a0aSReid Kleckner if (UseRange.isValid()) { 619dd8e0a0aSReid Kleckner if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(ReferringDecl)) { 620dd8e0a0aSReid Kleckner Selector Sel = MethodDecl->getSelector(); 621dd8e0a0aSReid Kleckner SmallVector<StringRef, 12> SelectorSlotNames; 6226ad0788cSKazu Hirata std::optional<unsigned> NumParams = tryParseObjCMethodName( 623dd8e0a0aSReid Kleckner Replacement, SelectorSlotNames, S.getLangOpts()); 624ca4af13eSKazu Hirata if (NumParams && *NumParams == Sel.getNumArgs()) { 625dd8e0a0aSReid Kleckner assert(SelectorSlotNames.size() == Locs.size()); 626dd8e0a0aSReid Kleckner for (unsigned I = 0; I < Locs.size(); ++I) { 627dd8e0a0aSReid Kleckner if (!Sel.getNameForSlot(I).empty()) { 628dd8e0a0aSReid Kleckner CharSourceRange NameRange = CharSourceRange::getCharRange( 629dd8e0a0aSReid Kleckner Locs[I], S.getLocForEndOfToken(Locs[I])); 630dd8e0a0aSReid Kleckner FixIts.push_back(FixItHint::CreateReplacement( 631dd8e0a0aSReid Kleckner NameRange, SelectorSlotNames[I])); 632dd8e0a0aSReid Kleckner } else 633dd8e0a0aSReid Kleckner FixIts.push_back( 634dd8e0a0aSReid Kleckner FixItHint::CreateInsertion(Locs[I], SelectorSlotNames[I])); 635dd8e0a0aSReid Kleckner } 636dd8e0a0aSReid Kleckner } else 637dd8e0a0aSReid Kleckner FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement)); 638dd8e0a0aSReid Kleckner } else 639dd8e0a0aSReid Kleckner FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement)); 640dd8e0a0aSReid Kleckner } 641dd8e0a0aSReid Kleckner } 642dd8e0a0aSReid Kleckner 643aafad2d2Scor3ntin // We emit deprecation warning for deprecated specializations 644aafad2d2Scor3ntin // when their instantiation stacks originate outside 645aafad2d2Scor3ntin // of a system header, even if the diagnostics is suppresed at the 646aafad2d2Scor3ntin // point of definition. 647aafad2d2Scor3ntin SourceLocation InstantiationLoc = 648aafad2d2Scor3ntin S.getTopMostPointOfInstantiation(ReferringDecl); 649aafad2d2Scor3ntin bool ShouldAllowWarningInSystemHeader = 650aafad2d2Scor3ntin InstantiationLoc != Loc && 651aafad2d2Scor3ntin !S.getSourceManager().isInSystemHeader(InstantiationLoc); 652aafad2d2Scor3ntin struct AllowWarningInSystemHeaders { 653aafad2d2Scor3ntin AllowWarningInSystemHeaders(DiagnosticsEngine &E, 654aafad2d2Scor3ntin bool AllowWarningInSystemHeaders) 655aafad2d2Scor3ntin : Engine(E), Prev(E.getSuppressSystemWarnings()) { 656aafad2d2Scor3ntin E.setSuppressSystemWarnings(!AllowWarningInSystemHeaders); 657aafad2d2Scor3ntin } 658aafad2d2Scor3ntin ~AllowWarningInSystemHeaders() { Engine.setSuppressSystemWarnings(Prev); } 659aafad2d2Scor3ntin 660aafad2d2Scor3ntin private: 661aafad2d2Scor3ntin DiagnosticsEngine &Engine; 662aafad2d2Scor3ntin bool Prev; 663aafad2d2Scor3ntin } SystemWarningOverrideRAII(S.getDiagnostics(), 664aafad2d2Scor3ntin ShouldAllowWarningInSystemHeader); 665aafad2d2Scor3ntin 666dd8e0a0aSReid Kleckner if (!Message.empty()) { 667dd8e0a0aSReid Kleckner S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts; 668dd8e0a0aSReid Kleckner if (ObjCProperty) 669dd8e0a0aSReid Kleckner S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) 670dd8e0a0aSReid Kleckner << ObjCProperty->getDeclName() << property_note_select; 671dd8e0a0aSReid Kleckner } else if (!UnknownObjCClass) { 672dd8e0a0aSReid Kleckner S.Diag(Loc, diag) << ReferringDecl << FixIts; 673dd8e0a0aSReid Kleckner if (ObjCProperty) 674dd8e0a0aSReid Kleckner S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) 675dd8e0a0aSReid Kleckner << ObjCProperty->getDeclName() << property_note_select; 676dd8e0a0aSReid Kleckner } else { 677dd8e0a0aSReid Kleckner S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts; 678dd8e0a0aSReid Kleckner S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); 679dd8e0a0aSReid Kleckner } 680dd8e0a0aSReid Kleckner 681dd8e0a0aSReid Kleckner S.Diag(NoteLocation, diag_available_here) 682dd8e0a0aSReid Kleckner << OffendingDecl << available_here_select_kind; 683dd8e0a0aSReid Kleckner } 684dd8e0a0aSReid Kleckner 685dd8e0a0aSReid Kleckner void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) { 686dd8e0a0aSReid Kleckner assert(DD.Kind == DelayedDiagnostic::Availability && 687dd8e0a0aSReid Kleckner "Expected an availability diagnostic here"); 688dd8e0a0aSReid Kleckner 689dd8e0a0aSReid Kleckner DD.Triggered = true; 690dd8e0a0aSReid Kleckner DoEmitAvailabilityWarning( 691dd8e0a0aSReid Kleckner *this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(), 692dd8e0a0aSReid Kleckner DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(), 693dd8e0a0aSReid Kleckner DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(), 694dd8e0a0aSReid Kleckner DD.getObjCProperty(), false); 695dd8e0a0aSReid Kleckner } 696dd8e0a0aSReid Kleckner 697dd8e0a0aSReid Kleckner static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR, 698dd8e0a0aSReid Kleckner const NamedDecl *ReferringDecl, 699dd8e0a0aSReid Kleckner const NamedDecl *OffendingDecl, 700dd8e0a0aSReid Kleckner StringRef Message, 701dd8e0a0aSReid Kleckner ArrayRef<SourceLocation> Locs, 702dd8e0a0aSReid Kleckner const ObjCInterfaceDecl *UnknownObjCClass, 703dd8e0a0aSReid Kleckner const ObjCPropertyDecl *ObjCProperty, 704dd8e0a0aSReid Kleckner bool ObjCPropertyAccess) { 705dd8e0a0aSReid Kleckner // Delay if we're currently parsing a declaration. 706dd8e0a0aSReid Kleckner if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { 707dd8e0a0aSReid Kleckner S.DelayedDiagnostics.add( 708dd8e0a0aSReid Kleckner DelayedDiagnostic::makeAvailability( 709dd8e0a0aSReid Kleckner AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass, 710dd8e0a0aSReid Kleckner ObjCProperty, Message, ObjCPropertyAccess)); 711dd8e0a0aSReid Kleckner return; 712dd8e0a0aSReid Kleckner } 713dd8e0a0aSReid Kleckner 714dd8e0a0aSReid Kleckner Decl *Ctx = cast<Decl>(S.getCurLexicalContext()); 715dd8e0a0aSReid Kleckner DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl, 716dd8e0a0aSReid Kleckner Message, Locs, UnknownObjCClass, ObjCProperty, 717dd8e0a0aSReid Kleckner ObjCPropertyAccess); 718dd8e0a0aSReid Kleckner } 719dd8e0a0aSReid Kleckner 720dd8e0a0aSReid Kleckner namespace { 721dd8e0a0aSReid Kleckner 722dd8e0a0aSReid Kleckner /// Returns true if the given statement can be a body-like child of \p Parent. 723dd8e0a0aSReid Kleckner bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) { 724dd8e0a0aSReid Kleckner switch (Parent->getStmtClass()) { 725dd8e0a0aSReid Kleckner case Stmt::IfStmtClass: 726dd8e0a0aSReid Kleckner return cast<IfStmt>(Parent)->getThen() == S || 727dd8e0a0aSReid Kleckner cast<IfStmt>(Parent)->getElse() == S; 728dd8e0a0aSReid Kleckner case Stmt::WhileStmtClass: 729dd8e0a0aSReid Kleckner return cast<WhileStmt>(Parent)->getBody() == S; 730dd8e0a0aSReid Kleckner case Stmt::DoStmtClass: 731dd8e0a0aSReid Kleckner return cast<DoStmt>(Parent)->getBody() == S; 732dd8e0a0aSReid Kleckner case Stmt::ForStmtClass: 733dd8e0a0aSReid Kleckner return cast<ForStmt>(Parent)->getBody() == S; 734dd8e0a0aSReid Kleckner case Stmt::CXXForRangeStmtClass: 735dd8e0a0aSReid Kleckner return cast<CXXForRangeStmt>(Parent)->getBody() == S; 736dd8e0a0aSReid Kleckner case Stmt::ObjCForCollectionStmtClass: 737dd8e0a0aSReid Kleckner return cast<ObjCForCollectionStmt>(Parent)->getBody() == S; 738dd8e0a0aSReid Kleckner case Stmt::CaseStmtClass: 739dd8e0a0aSReid Kleckner case Stmt::DefaultStmtClass: 740dd8e0a0aSReid Kleckner return cast<SwitchCase>(Parent)->getSubStmt() == S; 741dd8e0a0aSReid Kleckner default: 742dd8e0a0aSReid Kleckner return false; 743dd8e0a0aSReid Kleckner } 744dd8e0a0aSReid Kleckner } 745dd8e0a0aSReid Kleckner 746*dde802b1SSirraide class StmtUSEFinder : public DynamicRecursiveASTVisitor { 747dd8e0a0aSReid Kleckner const Stmt *Target; 748dd8e0a0aSReid Kleckner 749dd8e0a0aSReid Kleckner public: 750*dde802b1SSirraide bool VisitStmt(Stmt *S) override { return S != Target; } 751dd8e0a0aSReid Kleckner 752dd8e0a0aSReid Kleckner /// Returns true if the given statement is present in the given declaration. 753dd8e0a0aSReid Kleckner static bool isContained(const Stmt *Target, const Decl *D) { 754dd8e0a0aSReid Kleckner StmtUSEFinder Visitor; 755dd8e0a0aSReid Kleckner Visitor.Target = Target; 756dd8e0a0aSReid Kleckner return !Visitor.TraverseDecl(const_cast<Decl *>(D)); 757dd8e0a0aSReid Kleckner } 758dd8e0a0aSReid Kleckner }; 759dd8e0a0aSReid Kleckner 760dd8e0a0aSReid Kleckner /// Traverses the AST and finds the last statement that used a given 761dd8e0a0aSReid Kleckner /// declaration. 762*dde802b1SSirraide class LastDeclUSEFinder : public DynamicRecursiveASTVisitor { 763dd8e0a0aSReid Kleckner const Decl *D; 764dd8e0a0aSReid Kleckner 765dd8e0a0aSReid Kleckner public: 766*dde802b1SSirraide bool VisitDeclRefExpr(DeclRefExpr *DRE) override { 767dd8e0a0aSReid Kleckner if (DRE->getDecl() == D) 768dd8e0a0aSReid Kleckner return false; 769dd8e0a0aSReid Kleckner return true; 770dd8e0a0aSReid Kleckner } 771dd8e0a0aSReid Kleckner 772dd8e0a0aSReid Kleckner static const Stmt *findLastStmtThatUsesDecl(const Decl *D, 773dd8e0a0aSReid Kleckner const CompoundStmt *Scope) { 774dd8e0a0aSReid Kleckner LastDeclUSEFinder Visitor; 775dd8e0a0aSReid Kleckner Visitor.D = D; 77600ca004dSNico Weber for (const Stmt *S : llvm::reverse(Scope->body())) { 777dd8e0a0aSReid Kleckner if (!Visitor.TraverseStmt(const_cast<Stmt *>(S))) 778dd8e0a0aSReid Kleckner return S; 779dd8e0a0aSReid Kleckner } 780dd8e0a0aSReid Kleckner return nullptr; 781dd8e0a0aSReid Kleckner } 782dd8e0a0aSReid Kleckner }; 783dd8e0a0aSReid Kleckner 784dd8e0a0aSReid Kleckner /// This class implements -Wunguarded-availability. 785dd8e0a0aSReid Kleckner /// 786dd8e0a0aSReid Kleckner /// This is done with a traversal of the AST of a function that makes reference 787dd8e0a0aSReid Kleckner /// to a partially available declaration. Whenever we encounter an \c if of the 788dd8e0a0aSReid Kleckner /// form: \c if(@available(...)), we use the version from the condition to visit 789dd8e0a0aSReid Kleckner /// the then statement. 790*dde802b1SSirraide class DiagnoseUnguardedAvailability : public DynamicRecursiveASTVisitor { 791dd8e0a0aSReid Kleckner Sema &SemaRef; 792dd8e0a0aSReid Kleckner Decl *Ctx; 793dd8e0a0aSReid Kleckner 794dd8e0a0aSReid Kleckner /// Stack of potentially nested 'if (@available(...))'s. 795dd8e0a0aSReid Kleckner SmallVector<VersionTuple, 8> AvailabilityStack; 796dd8e0a0aSReid Kleckner SmallVector<const Stmt *, 16> StmtStack; 797dd8e0a0aSReid Kleckner 798dd8e0a0aSReid Kleckner void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range, 799dd8e0a0aSReid Kleckner ObjCInterfaceDecl *ClassReceiver = nullptr); 800dd8e0a0aSReid Kleckner 801dd8e0a0aSReid Kleckner public: 802dd8e0a0aSReid Kleckner DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx) 803dd8e0a0aSReid Kleckner : SemaRef(SemaRef), Ctx(Ctx) { 804dd8e0a0aSReid Kleckner AvailabilityStack.push_back( 805dd8e0a0aSReid Kleckner SemaRef.Context.getTargetInfo().getPlatformMinVersion()); 806dd8e0a0aSReid Kleckner } 807dd8e0a0aSReid Kleckner 808*dde802b1SSirraide bool TraverseStmt(Stmt *S) override { 809dd8e0a0aSReid Kleckner if (!S) 810dd8e0a0aSReid Kleckner return true; 811dd8e0a0aSReid Kleckner StmtStack.push_back(S); 812*dde802b1SSirraide bool Result = DynamicRecursiveASTVisitor::TraverseStmt(S); 813dd8e0a0aSReid Kleckner StmtStack.pop_back(); 814dd8e0a0aSReid Kleckner return Result; 815dd8e0a0aSReid Kleckner } 816dd8e0a0aSReid Kleckner 817dd8e0a0aSReid Kleckner void IssueDiagnostics(Stmt *S) { TraverseStmt(S); } 818dd8e0a0aSReid Kleckner 819*dde802b1SSirraide bool TraverseIfStmt(IfStmt *If) override; 820dd8e0a0aSReid Kleckner 821dd8e0a0aSReid Kleckner // for 'case X:' statements, don't bother looking at the 'X'; it can't lead 822dd8e0a0aSReid Kleckner // to any useful diagnostics. 823*dde802b1SSirraide bool TraverseCaseStmt(CaseStmt *CS) override { 824*dde802b1SSirraide return TraverseStmt(CS->getSubStmt()); 825*dde802b1SSirraide } 826dd8e0a0aSReid Kleckner 827*dde802b1SSirraide bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) override { 828dd8e0a0aSReid Kleckner if (ObjCMethodDecl *D = Msg->getMethodDecl()) { 829dd8e0a0aSReid Kleckner ObjCInterfaceDecl *ID = nullptr; 830dd8e0a0aSReid Kleckner QualType ReceiverTy = Msg->getClassReceiver(); 831dd8e0a0aSReid Kleckner if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType()) 832dd8e0a0aSReid Kleckner ID = ReceiverTy->getAsObjCInterfaceType()->getInterface(); 833dd8e0a0aSReid Kleckner 834dd8e0a0aSReid Kleckner DiagnoseDeclAvailability( 835dd8e0a0aSReid Kleckner D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID); 836dd8e0a0aSReid Kleckner } 837dd8e0a0aSReid Kleckner return true; 838dd8e0a0aSReid Kleckner } 839dd8e0a0aSReid Kleckner 840*dde802b1SSirraide bool VisitDeclRefExpr(DeclRefExpr *DRE) override { 841dd8e0a0aSReid Kleckner DiagnoseDeclAvailability(DRE->getDecl(), 842dd8e0a0aSReid Kleckner SourceRange(DRE->getBeginLoc(), DRE->getEndLoc())); 843dd8e0a0aSReid Kleckner return true; 844dd8e0a0aSReid Kleckner } 845dd8e0a0aSReid Kleckner 846*dde802b1SSirraide bool VisitMemberExpr(MemberExpr *ME) override { 847dd8e0a0aSReid Kleckner DiagnoseDeclAvailability(ME->getMemberDecl(), 848dd8e0a0aSReid Kleckner SourceRange(ME->getBeginLoc(), ME->getEndLoc())); 849dd8e0a0aSReid Kleckner return true; 850dd8e0a0aSReid Kleckner } 851dd8e0a0aSReid Kleckner 852*dde802b1SSirraide bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) override { 853dd8e0a0aSReid Kleckner SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use) 854dd8e0a0aSReid Kleckner << (!SemaRef.getLangOpts().ObjC); 855dd8e0a0aSReid Kleckner return true; 856dd8e0a0aSReid Kleckner } 857dd8e0a0aSReid Kleckner 858*dde802b1SSirraide bool VisitTypeLoc(TypeLoc Ty) override; 859dd8e0a0aSReid Kleckner }; 860dd8e0a0aSReid Kleckner 861dd8e0a0aSReid Kleckner void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( 862dd8e0a0aSReid Kleckner NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) { 863dd8e0a0aSReid Kleckner AvailabilityResult Result; 864dd8e0a0aSReid Kleckner const NamedDecl *OffendingDecl; 865dd8e0a0aSReid Kleckner std::tie(Result, OffendingDecl) = 866dd8e0a0aSReid Kleckner ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass); 867dd8e0a0aSReid Kleckner if (Result != AR_Available) { 868dd8e0a0aSReid Kleckner // All other diagnostic kinds have already been handled in 869dd8e0a0aSReid Kleckner // DiagnoseAvailabilityOfDecl. 870dd8e0a0aSReid Kleckner if (Result != AR_NotYetIntroduced) 871dd8e0a0aSReid Kleckner return; 872dd8e0a0aSReid Kleckner 873dd8e0a0aSReid Kleckner const AvailabilityAttr *AA = 874dd8e0a0aSReid Kleckner getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl); 8751c0e7221Ssmanna12 assert(AA != nullptr && "expecting valid availability attribute"); 8763f33c4c1SHelena Kotas bool EnvironmentMatchesOrNone = 8773f33c4c1SHelena Kotas hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA); 878dd8e0a0aSReid Kleckner VersionTuple Introduced = AA->getIntroduced(); 879dd8e0a0aSReid Kleckner 8803f33c4c1SHelena Kotas if (EnvironmentMatchesOrNone && AvailabilityStack.back() >= Introduced) 881dd8e0a0aSReid Kleckner return; 882dd8e0a0aSReid Kleckner 883dd8e0a0aSReid Kleckner // If the context of this function is less available than D, we should not 884dd8e0a0aSReid Kleckner // emit a diagnostic. 8853f33c4c1SHelena Kotas if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, 8863f33c4c1SHelena Kotas AA->getEnvironment(), Ctx, 887dd8e0a0aSReid Kleckner OffendingDecl)) 888dd8e0a0aSReid Kleckner return; 889dd8e0a0aSReid Kleckner 8903f33c4c1SHelena Kotas const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo(); 8913f33c4c1SHelena Kotas std::string PlatformName( 8923f33c4c1SHelena Kotas AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); 89330efdce7SHelena Kotas llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName()); 8943f33c4c1SHelena Kotas llvm::StringRef AttrEnvironment = 89530efdce7SHelena Kotas AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; 8963f33c4c1SHelena Kotas bool UseEnvironment = 8973f33c4c1SHelena Kotas (!AttrEnvironment.empty() && !TargetEnvironment.empty()); 8983f33c4c1SHelena Kotas 89930efdce7SHelena Kotas unsigned DiagKind = getAvailabilityDiagnosticKind( 90030efdce7SHelena Kotas SemaRef.Context, 90130efdce7SHelena Kotas SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced, 90230efdce7SHelena Kotas EnvironmentMatchesOrNone); 903dd8e0a0aSReid Kleckner 904dd8e0a0aSReid Kleckner SemaRef.Diag(Range.getBegin(), DiagKind) 9053f33c4c1SHelena Kotas << Range << D << PlatformName << Introduced.getAsString() 9063f33c4c1SHelena Kotas << UseEnvironment << TargetEnvironment; 907dd8e0a0aSReid Kleckner 908dd8e0a0aSReid Kleckner SemaRef.Diag(OffendingDecl->getLocation(), 909dd8e0a0aSReid Kleckner diag::note_partial_availability_specified_here) 910dd8e0a0aSReid Kleckner << OffendingDecl << PlatformName << Introduced.getAsString() 9113f33c4c1SHelena Kotas << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString() 9123f33c4c1SHelena Kotas << UseEnvironment << AttrEnvironment << TargetEnvironment; 913dd8e0a0aSReid Kleckner 9148890209eSHelena Kotas // Do not offer to silence the warning or fixits for HLSL 9158890209eSHelena Kotas if (SemaRef.getLangOpts().HLSL) 9168890209eSHelena Kotas return; 9178890209eSHelena Kotas 918dd8e0a0aSReid Kleckner auto FixitDiag = 919dd8e0a0aSReid Kleckner SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) 920dd8e0a0aSReid Kleckner << Range << D 921dd8e0a0aSReid Kleckner << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0 922dd8e0a0aSReid Kleckner : /*__builtin_available*/ 1); 923dd8e0a0aSReid Kleckner 924dd8e0a0aSReid Kleckner // Find the statement which should be enclosed in the if @available check. 925dd8e0a0aSReid Kleckner if (StmtStack.empty()) 926dd8e0a0aSReid Kleckner return; 927dd8e0a0aSReid Kleckner const Stmt *StmtOfUse = StmtStack.back(); 928dd8e0a0aSReid Kleckner const CompoundStmt *Scope = nullptr; 929dd8e0a0aSReid Kleckner for (const Stmt *S : llvm::reverse(StmtStack)) { 930dd8e0a0aSReid Kleckner if (const auto *CS = dyn_cast<CompoundStmt>(S)) { 931dd8e0a0aSReid Kleckner Scope = CS; 932dd8e0a0aSReid Kleckner break; 933dd8e0a0aSReid Kleckner } 934dd8e0a0aSReid Kleckner if (isBodyLikeChildStmt(StmtOfUse, S)) { 935dd8e0a0aSReid Kleckner // The declaration won't be seen outside of the statement, so we don't 936dd8e0a0aSReid Kleckner // have to wrap the uses of any declared variables in if (@available). 937dd8e0a0aSReid Kleckner // Therefore we can avoid setting Scope here. 938dd8e0a0aSReid Kleckner break; 939dd8e0a0aSReid Kleckner } 940dd8e0a0aSReid Kleckner StmtOfUse = S; 941dd8e0a0aSReid Kleckner } 942dd8e0a0aSReid Kleckner const Stmt *LastStmtOfUse = nullptr; 943dd8e0a0aSReid Kleckner if (isa<DeclStmt>(StmtOfUse) && Scope) { 944dd8e0a0aSReid Kleckner for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) { 945dd8e0a0aSReid Kleckner if (StmtUSEFinder::isContained(StmtStack.back(), D)) { 946dd8e0a0aSReid Kleckner LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope); 947dd8e0a0aSReid Kleckner break; 948dd8e0a0aSReid Kleckner } 949dd8e0a0aSReid Kleckner } 950dd8e0a0aSReid Kleckner } 951dd8e0a0aSReid Kleckner 952dd8e0a0aSReid Kleckner const SourceManager &SM = SemaRef.getSourceManager(); 953dd8e0a0aSReid Kleckner SourceLocation IfInsertionLoc = 954dd8e0a0aSReid Kleckner SM.getExpansionLoc(StmtOfUse->getBeginLoc()); 955dd8e0a0aSReid Kleckner SourceLocation StmtEndLoc = 956dd8e0a0aSReid Kleckner SM.getExpansionRange( 957dd8e0a0aSReid Kleckner (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc()) 958dd8e0a0aSReid Kleckner .getEnd(); 959dd8e0a0aSReid Kleckner if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc)) 960dd8e0a0aSReid Kleckner return; 961dd8e0a0aSReid Kleckner 962dd8e0a0aSReid Kleckner StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM); 963dd8e0a0aSReid Kleckner const char *ExtraIndentation = " "; 964dd8e0a0aSReid Kleckner std::string FixItString; 965dd8e0a0aSReid Kleckner llvm::raw_string_ostream FixItOS(FixItString); 966dd8e0a0aSReid Kleckner FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available" 967dd8e0a0aSReid Kleckner : "__builtin_available") 968dd8e0a0aSReid Kleckner << "(" 969dd8e0a0aSReid Kleckner << AvailabilityAttr::getPlatformNameSourceSpelling( 970dd8e0a0aSReid Kleckner SemaRef.getASTContext().getTargetInfo().getPlatformName()) 971dd8e0a0aSReid Kleckner << " " << Introduced.getAsString() << ", *)) {\n" 972dd8e0a0aSReid Kleckner << Indentation << ExtraIndentation; 973dd8e0a0aSReid Kleckner FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str()); 974dd8e0a0aSReid Kleckner SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken( 975dd8e0a0aSReid Kleckner StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(), 976dd8e0a0aSReid Kleckner /*SkipTrailingWhitespaceAndNewLine=*/false); 977dd8e0a0aSReid Kleckner if (ElseInsertionLoc.isInvalid()) 978dd8e0a0aSReid Kleckner ElseInsertionLoc = 979dd8e0a0aSReid Kleckner Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts()); 980dd8e0a0aSReid Kleckner FixItOS.str().clear(); 981dd8e0a0aSReid Kleckner FixItOS << "\n" 982dd8e0a0aSReid Kleckner << Indentation << "} else {\n" 983dd8e0a0aSReid Kleckner << Indentation << ExtraIndentation 984dd8e0a0aSReid Kleckner << "// Fallback on earlier versions\n" 985dd8e0a0aSReid Kleckner << Indentation << "}"; 986dd8e0a0aSReid Kleckner FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str()); 987dd8e0a0aSReid Kleckner } 988dd8e0a0aSReid Kleckner } 989dd8e0a0aSReid Kleckner 990dd8e0a0aSReid Kleckner bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { 991dd8e0a0aSReid Kleckner const Type *TyPtr = Ty.getTypePtr(); 992dd8e0a0aSReid Kleckner SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()}; 993dd8e0a0aSReid Kleckner 994dd8e0a0aSReid Kleckner if (Range.isInvalid()) 995dd8e0a0aSReid Kleckner return true; 996dd8e0a0aSReid Kleckner 997dd8e0a0aSReid Kleckner if (const auto *TT = dyn_cast<TagType>(TyPtr)) { 998dd8e0a0aSReid Kleckner TagDecl *TD = TT->getDecl(); 999dd8e0a0aSReid Kleckner DiagnoseDeclAvailability(TD, Range); 1000dd8e0a0aSReid Kleckner 1001dd8e0a0aSReid Kleckner } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) { 1002dd8e0a0aSReid Kleckner TypedefNameDecl *D = TD->getDecl(); 1003dd8e0a0aSReid Kleckner DiagnoseDeclAvailability(D, Range); 1004dd8e0a0aSReid Kleckner 1005dd8e0a0aSReid Kleckner } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) { 1006dd8e0a0aSReid Kleckner if (NamedDecl *D = ObjCO->getInterface()) 1007dd8e0a0aSReid Kleckner DiagnoseDeclAvailability(D, Range); 1008dd8e0a0aSReid Kleckner } 1009dd8e0a0aSReid Kleckner 1010dd8e0a0aSReid Kleckner return true; 1011dd8e0a0aSReid Kleckner } 1012dd8e0a0aSReid Kleckner 10131553cb5dSGeorge Burgess IV struct ExtractedAvailabilityExpr { 10141553cb5dSGeorge Burgess IV const ObjCAvailabilityCheckExpr *E = nullptr; 10151553cb5dSGeorge Burgess IV bool isNegated = false; 10161553cb5dSGeorge Burgess IV }; 1017dd8e0a0aSReid Kleckner 10181553cb5dSGeorge Burgess IV ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) { 10191553cb5dSGeorge Burgess IV const auto *E = IfCond; 10201553cb5dSGeorge Burgess IV bool IsNegated = false; 10211553cb5dSGeorge Burgess IV while (true) { 10221553cb5dSGeorge Burgess IV E = E->IgnoreParens(); 10231553cb5dSGeorge Burgess IV if (const auto *AE = dyn_cast<ObjCAvailabilityCheckExpr>(E)) { 10241553cb5dSGeorge Burgess IV return ExtractedAvailabilityExpr{AE, IsNegated}; 10251553cb5dSGeorge Burgess IV } 10261553cb5dSGeorge Burgess IV 10271553cb5dSGeorge Burgess IV const auto *UO = dyn_cast<UnaryOperator>(E); 10281553cb5dSGeorge Burgess IV if (!UO || UO->getOpcode() != UO_LNot) { 10291553cb5dSGeorge Burgess IV return ExtractedAvailabilityExpr{}; 10301553cb5dSGeorge Burgess IV } 10311553cb5dSGeorge Burgess IV E = UO->getSubExpr(); 10321553cb5dSGeorge Burgess IV IsNegated = !IsNegated; 10331553cb5dSGeorge Burgess IV } 10341553cb5dSGeorge Burgess IV } 10351553cb5dSGeorge Burgess IV 10361553cb5dSGeorge Burgess IV bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { 10371553cb5dSGeorge Burgess IV ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(If->getCond()); 10381553cb5dSGeorge Burgess IV if (!IfCond.E) { 1039dd8e0a0aSReid Kleckner // This isn't an availability checking 'if', we can just continue. 1040*dde802b1SSirraide return DynamicRecursiveASTVisitor::TraverseIfStmt(If); 1041dd8e0a0aSReid Kleckner } 1042dd8e0a0aSReid Kleckner 10431553cb5dSGeorge Burgess IV VersionTuple CondVersion = IfCond.E->getVersion(); 10441553cb5dSGeorge Burgess IV // If we're using the '*' case here or if this check is redundant, then we 10451553cb5dSGeorge Burgess IV // use the enclosing version to check both branches. 10461553cb5dSGeorge Burgess IV if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) { 10471553cb5dSGeorge Burgess IV return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); 10481553cb5dSGeorge Burgess IV } 10491553cb5dSGeorge Burgess IV 10501553cb5dSGeorge Burgess IV auto *Guarded = If->getThen(); 10511553cb5dSGeorge Burgess IV auto *Unguarded = If->getElse(); 10521553cb5dSGeorge Burgess IV if (IfCond.isNegated) { 10531553cb5dSGeorge Burgess IV std::swap(Guarded, Unguarded); 10541553cb5dSGeorge Burgess IV } 10551553cb5dSGeorge Burgess IV 1056dd8e0a0aSReid Kleckner AvailabilityStack.push_back(CondVersion); 10571553cb5dSGeorge Burgess IV bool ShouldContinue = TraverseStmt(Guarded); 1058dd8e0a0aSReid Kleckner AvailabilityStack.pop_back(); 1059dd8e0a0aSReid Kleckner 10601553cb5dSGeorge Burgess IV return ShouldContinue && TraverseStmt(Unguarded); 1061dd8e0a0aSReid Kleckner } 1062dd8e0a0aSReid Kleckner 1063dd8e0a0aSReid Kleckner } // end anonymous namespace 1064dd8e0a0aSReid Kleckner 1065dd8e0a0aSReid Kleckner void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { 1066dd8e0a0aSReid Kleckner Stmt *Body = nullptr; 1067dd8e0a0aSReid Kleckner 1068dd8e0a0aSReid Kleckner if (auto *FD = D->getAsFunction()) { 1069dd8e0a0aSReid Kleckner Body = FD->getBody(); 1070143ec502SAkira Hatanaka 1071143ec502SAkira Hatanaka if (auto *CD = dyn_cast<CXXConstructorDecl>(FD)) 1072143ec502SAkira Hatanaka for (const CXXCtorInitializer *CI : CD->inits()) 1073143ec502SAkira Hatanaka DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(CI->getInit()); 1074143ec502SAkira Hatanaka 1075dd8e0a0aSReid Kleckner } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) 1076dd8e0a0aSReid Kleckner Body = MD->getBody(); 1077dd8e0a0aSReid Kleckner else if (auto *BD = dyn_cast<BlockDecl>(D)) 1078dd8e0a0aSReid Kleckner Body = BD->getBody(); 1079dd8e0a0aSReid Kleckner 1080dd8e0a0aSReid Kleckner assert(Body && "Need a body here!"); 1081dd8e0a0aSReid Kleckner 1082dd8e0a0aSReid Kleckner DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body); 1083dd8e0a0aSReid Kleckner } 1084dd8e0a0aSReid Kleckner 1085a5a3efa8SLogan Smith FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() { 1086a5a3efa8SLogan Smith if (FunctionScopes.empty()) 1087a5a3efa8SLogan Smith return nullptr; 1088a5a3efa8SLogan Smith 1089a5a3efa8SLogan Smith // Conservatively search the entire current function scope context for 1090a5a3efa8SLogan Smith // availability violations. This ensures we always correctly analyze nested 1091a5a3efa8SLogan Smith // classes, blocks, lambdas, etc. that may or may not be inside if(@available) 1092a5a3efa8SLogan Smith // checks themselves. 1093a5a3efa8SLogan Smith return FunctionScopes.front(); 1094a5a3efa8SLogan Smith } 1095a5a3efa8SLogan Smith 1096dd8e0a0aSReid Kleckner void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, 1097dd8e0a0aSReid Kleckner ArrayRef<SourceLocation> Locs, 1098dd8e0a0aSReid Kleckner const ObjCInterfaceDecl *UnknownObjCClass, 1099dd8e0a0aSReid Kleckner bool ObjCPropertyAccess, 1100dd8e0a0aSReid Kleckner bool AvoidPartialAvailabilityChecks, 1101dd8e0a0aSReid Kleckner ObjCInterfaceDecl *ClassReceiver) { 1102dd8e0a0aSReid Kleckner std::string Message; 1103dd8e0a0aSReid Kleckner AvailabilityResult Result; 1104dd8e0a0aSReid Kleckner const NamedDecl* OffendingDecl; 1105dd8e0a0aSReid Kleckner // See if this declaration is unavailable, deprecated, or partial. 1106dd8e0a0aSReid Kleckner std::tie(Result, OffendingDecl) = 1107dd8e0a0aSReid Kleckner ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver); 1108dd8e0a0aSReid Kleckner if (Result == AR_Available) 1109dd8e0a0aSReid Kleckner return; 1110dd8e0a0aSReid Kleckner 1111dd8e0a0aSReid Kleckner if (Result == AR_NotYetIntroduced) { 1112dd8e0a0aSReid Kleckner if (AvoidPartialAvailabilityChecks) 1113dd8e0a0aSReid Kleckner return; 1114dd8e0a0aSReid Kleckner 1115dd8e0a0aSReid Kleckner // We need to know the @available context in the current function to 1116dd8e0a0aSReid Kleckner // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that 1117dd8e0a0aSReid Kleckner // when we're done parsing the current function. 1118a5a3efa8SLogan Smith if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) { 1119a5a3efa8SLogan Smith Context->HasPotentialAvailabilityViolations = true; 1120dd8e0a0aSReid Kleckner return; 1121dd8e0a0aSReid Kleckner } 1122dd8e0a0aSReid Kleckner } 1123dd8e0a0aSReid Kleckner 1124dd8e0a0aSReid Kleckner const ObjCPropertyDecl *ObjCPDecl = nullptr; 1125dd8e0a0aSReid Kleckner if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { 1126dd8e0a0aSReid Kleckner if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { 1127dd8e0a0aSReid Kleckner AvailabilityResult PDeclResult = PD->getAvailability(nullptr); 1128dd8e0a0aSReid Kleckner if (PDeclResult == Result) 1129dd8e0a0aSReid Kleckner ObjCPDecl = PD; 1130dd8e0a0aSReid Kleckner } 1131dd8e0a0aSReid Kleckner } 1132dd8e0a0aSReid Kleckner 1133dd8e0a0aSReid Kleckner EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs, 1134dd8e0a0aSReid Kleckner UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); 1135dd8e0a0aSReid Kleckner } 1136