15ffd83dbSDimitry Andric //===--- SemaAvailability.cpp - Availability attribute handling -----------===// 25ffd83dbSDimitry Andric // 35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ffd83dbSDimitry Andric // 75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 85ffd83dbSDimitry Andric // 95ffd83dbSDimitry Andric // This file processes the availability attribute. 105ffd83dbSDimitry Andric // 115ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 125ffd83dbSDimitry Andric 135ffd83dbSDimitry Andric #include "clang/AST/Attr.h" 145ffd83dbSDimitry Andric #include "clang/AST/Decl.h" 15*0fca6ea1SDimitry Andric #include "clang/AST/DeclTemplate.h" 165ffd83dbSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h" 175ffd83dbSDimitry Andric #include "clang/Basic/DiagnosticSema.h" 18*0fca6ea1SDimitry Andric #include "clang/Basic/IdentifierTable.h" 19*0fca6ea1SDimitry Andric #include "clang/Basic/LangOptions.h" 205ffd83dbSDimitry Andric #include "clang/Basic/TargetInfo.h" 215ffd83dbSDimitry Andric #include "clang/Lex/Preprocessor.h" 225ffd83dbSDimitry Andric #include "clang/Sema/DelayedDiagnostic.h" 235ffd83dbSDimitry Andric #include "clang/Sema/ScopeInfo.h" 245ffd83dbSDimitry Andric #include "clang/Sema/Sema.h" 25*0fca6ea1SDimitry Andric #include "clang/Sema/SemaObjC.h" 26*0fca6ea1SDimitry Andric #include "llvm/ADT/StringRef.h" 27bdd1243dSDimitry Andric #include <optional> 285ffd83dbSDimitry Andric 295ffd83dbSDimitry Andric using namespace clang; 305ffd83dbSDimitry Andric using namespace sema; 315ffd83dbSDimitry Andric 32*0fca6ea1SDimitry Andric static bool hasMatchingEnvironmentOrNone(const ASTContext &Context, 33*0fca6ea1SDimitry Andric const AvailabilityAttr *AA) { 34*0fca6ea1SDimitry Andric IdentifierInfo *IIEnvironment = AA->getEnvironment(); 35*0fca6ea1SDimitry Andric auto Environment = Context.getTargetInfo().getTriple().getEnvironment(); 36*0fca6ea1SDimitry Andric if (!IIEnvironment || Environment == llvm::Triple::UnknownEnvironment) 37*0fca6ea1SDimitry Andric return true; 38*0fca6ea1SDimitry Andric 39*0fca6ea1SDimitry Andric llvm::Triple::EnvironmentType ET = 40*0fca6ea1SDimitry Andric AvailabilityAttr::getEnvironmentType(IIEnvironment->getName()); 41*0fca6ea1SDimitry Andric return Environment == ET; 42*0fca6ea1SDimitry Andric } 43*0fca6ea1SDimitry Andric 445ffd83dbSDimitry Andric static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, 455ffd83dbSDimitry Andric const Decl *D) { 46*0fca6ea1SDimitry Andric AvailabilityAttr const *PartialMatch = nullptr; 475ffd83dbSDimitry Andric // Check each AvailabilityAttr to find the one for this platform. 48*0fca6ea1SDimitry Andric // For multiple attributes with the same platform try to find one for this 49*0fca6ea1SDimitry Andric // environment. 50*0fca6ea1SDimitry Andric // The attribute is always on the FunctionDecl, not on the 51*0fca6ea1SDimitry Andric // FunctionTemplateDecl. 52*0fca6ea1SDimitry Andric if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D)) 53*0fca6ea1SDimitry Andric D = FTD->getTemplatedDecl(); 545ffd83dbSDimitry Andric for (const auto *A : D->attrs()) { 555ffd83dbSDimitry Andric if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) { 565ffd83dbSDimitry Andric // FIXME: this is copied from CheckAvailability. We should try to 575ffd83dbSDimitry Andric // de-duplicate. 585ffd83dbSDimitry Andric 595ffd83dbSDimitry Andric // Check if this is an App Extension "platform", and if so chop off 605ffd83dbSDimitry Andric // the suffix for matching with the actual platform. 615ffd83dbSDimitry Andric StringRef ActualPlatform = Avail->getPlatform()->getName(); 625ffd83dbSDimitry Andric StringRef RealizedPlatform = ActualPlatform; 635ffd83dbSDimitry Andric if (Context.getLangOpts().AppExt) { 645ffd83dbSDimitry Andric size_t suffix = RealizedPlatform.rfind("_app_extension"); 655ffd83dbSDimitry Andric if (suffix != StringRef::npos) 665ffd83dbSDimitry Andric RealizedPlatform = RealizedPlatform.slice(0, suffix); 675ffd83dbSDimitry Andric } 685ffd83dbSDimitry Andric 695ffd83dbSDimitry Andric StringRef TargetPlatform = Context.getTargetInfo().getPlatformName(); 705ffd83dbSDimitry Andric 715ffd83dbSDimitry Andric // Match the platform name. 72*0fca6ea1SDimitry Andric if (RealizedPlatform == TargetPlatform) { 73*0fca6ea1SDimitry Andric // Find the best matching attribute for this environment 74*0fca6ea1SDimitry Andric if (hasMatchingEnvironmentOrNone(Context, Avail)) 755ffd83dbSDimitry Andric return Avail; 76*0fca6ea1SDimitry Andric PartialMatch = Avail; 775ffd83dbSDimitry Andric } 785ffd83dbSDimitry Andric } 79*0fca6ea1SDimitry Andric } 80*0fca6ea1SDimitry Andric return PartialMatch; 815ffd83dbSDimitry Andric } 825ffd83dbSDimitry Andric 835ffd83dbSDimitry Andric /// The diagnostic we should emit for \c D, and the declaration that 845ffd83dbSDimitry Andric /// originated it, or \c AR_Available. 855ffd83dbSDimitry Andric /// 865ffd83dbSDimitry Andric /// \param D The declaration to check. 875ffd83dbSDimitry Andric /// \param Message If non-null, this will be populated with the message from 885ffd83dbSDimitry Andric /// the availability attribute that is selected. 89349cc55cSDimitry Andric /// \param ClassReceiver If we're checking the method of a class message 905ffd83dbSDimitry Andric /// send, the class. Otherwise nullptr. 915ffd83dbSDimitry Andric static std::pair<AvailabilityResult, const NamedDecl *> 925ffd83dbSDimitry Andric ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D, 935ffd83dbSDimitry Andric std::string *Message, 945ffd83dbSDimitry Andric ObjCInterfaceDecl *ClassReceiver) { 955ffd83dbSDimitry Andric AvailabilityResult Result = D->getAvailability(Message); 965ffd83dbSDimitry Andric 975ffd83dbSDimitry Andric // For typedefs, if the typedef declaration appears available look 985ffd83dbSDimitry Andric // to the underlying type to see if it is more restrictive. 995ffd83dbSDimitry Andric while (const auto *TD = dyn_cast<TypedefNameDecl>(D)) { 1005ffd83dbSDimitry Andric if (Result == AR_Available) { 1015ffd83dbSDimitry Andric if (const auto *TT = TD->getUnderlyingType()->getAs<TagType>()) { 1025ffd83dbSDimitry Andric D = TT->getDecl(); 1035ffd83dbSDimitry Andric Result = D->getAvailability(Message); 1045ffd83dbSDimitry Andric continue; 1055ffd83dbSDimitry Andric } 1065ffd83dbSDimitry Andric } 1075ffd83dbSDimitry Andric break; 1085ffd83dbSDimitry Andric } 1095ffd83dbSDimitry Andric 110*0fca6ea1SDimitry Andric // For alias templates, get the underlying declaration. 111*0fca6ea1SDimitry Andric if (const auto *ADecl = dyn_cast<TypeAliasTemplateDecl>(D)) { 112*0fca6ea1SDimitry Andric D = ADecl->getTemplatedDecl(); 113*0fca6ea1SDimitry Andric Result = D->getAvailability(Message); 114*0fca6ea1SDimitry Andric } 115*0fca6ea1SDimitry Andric 1165ffd83dbSDimitry Andric // Forward class declarations get their attributes from their definition. 1175ffd83dbSDimitry Andric if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) { 1185ffd83dbSDimitry Andric if (IDecl->getDefinition()) { 1195ffd83dbSDimitry Andric D = IDecl->getDefinition(); 1205ffd83dbSDimitry Andric Result = D->getAvailability(Message); 1215ffd83dbSDimitry Andric } 1225ffd83dbSDimitry Andric } 1235ffd83dbSDimitry Andric 1245ffd83dbSDimitry Andric if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) 1255ffd83dbSDimitry Andric if (Result == AR_Available) { 1265ffd83dbSDimitry Andric const DeclContext *DC = ECD->getDeclContext(); 1275ffd83dbSDimitry Andric if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) { 1285ffd83dbSDimitry Andric Result = TheEnumDecl->getAvailability(Message); 1295ffd83dbSDimitry Andric D = TheEnumDecl; 1305ffd83dbSDimitry Andric } 1315ffd83dbSDimitry Andric } 1325ffd83dbSDimitry Andric 1335ffd83dbSDimitry Andric // For +new, infer availability from -init. 1345ffd83dbSDimitry Andric if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { 135*0fca6ea1SDimitry Andric if (S.ObjC().NSAPIObj && ClassReceiver) { 1365ffd83dbSDimitry Andric ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod( 137*0fca6ea1SDimitry Andric S.ObjC().NSAPIObj->getInitSelector()); 1385ffd83dbSDimitry Andric if (Init && Result == AR_Available && MD->isClassMethod() && 139*0fca6ea1SDimitry Andric MD->getSelector() == S.ObjC().NSAPIObj->getNewSelector() && 1405ffd83dbSDimitry Andric MD->definedInNSObject(S.getASTContext())) { 1415ffd83dbSDimitry Andric Result = Init->getAvailability(Message); 1425ffd83dbSDimitry Andric D = Init; 1435ffd83dbSDimitry Andric } 1445ffd83dbSDimitry Andric } 1455ffd83dbSDimitry Andric } 1465ffd83dbSDimitry Andric 1475ffd83dbSDimitry Andric return {Result, D}; 1485ffd83dbSDimitry Andric } 1495ffd83dbSDimitry Andric 1505ffd83dbSDimitry Andric 1515ffd83dbSDimitry Andric /// whether we should emit a diagnostic for \c K and \c DeclVersion in 1525ffd83dbSDimitry Andric /// the context of \c Ctx. For example, we should emit an unavailable diagnostic 1535ffd83dbSDimitry Andric /// in a deprecated context, but not the other way around. 154*0fca6ea1SDimitry Andric static bool ShouldDiagnoseAvailabilityInContext( 155*0fca6ea1SDimitry Andric Sema &S, AvailabilityResult K, VersionTuple DeclVersion, 156*0fca6ea1SDimitry Andric const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) { 1575ffd83dbSDimitry Andric assert(K != AR_Available && "Expected an unavailable declaration here!"); 1585ffd83dbSDimitry Andric 1598a4dda33SDimitry Andric // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic. 1608a4dda33SDimitry Andric auto DeclLoc = Ctx->getBeginLoc(); 1618a4dda33SDimitry Andric // This is only a problem in Foundation's C++ implementation for CF_OPTIONS. 1628a4dda33SDimitry Andric if (DeclLoc.isMacroID() && S.getLangOpts().CPlusPlus && 1638a4dda33SDimitry Andric isa<TypedefDecl>(OffendingDecl)) { 1648a4dda33SDimitry Andric StringRef MacroName = S.getPreprocessor().getImmediateMacroName(DeclLoc); 1658a4dda33SDimitry Andric if (MacroName == "CF_OPTIONS" || MacroName == "OBJC_OPTIONS" || 1668a4dda33SDimitry Andric MacroName == "SWIFT_OPTIONS" || MacroName == "NS_OPTIONS") { 1678a4dda33SDimitry Andric return false; 1688a4dda33SDimitry Andric } 1698a4dda33SDimitry Andric } 1708a4dda33SDimitry Andric 171*0fca6ea1SDimitry Andric // In HLSL, skip emitting diagnostic if the diagnostic mode is not set to 172*0fca6ea1SDimitry Andric // strict (-fhlsl-strict-availability), or if the target is library and the 173*0fca6ea1SDimitry Andric // availability is restricted to a specific environment/shader stage. 174*0fca6ea1SDimitry Andric // For libraries the availability will be checked later in 175*0fca6ea1SDimitry Andric // DiagnoseHLSLAvailability class once where the specific environment/shader 176*0fca6ea1SDimitry Andric // stage of the caller is known. 177*0fca6ea1SDimitry Andric if (S.getLangOpts().HLSL) { 178*0fca6ea1SDimitry Andric if (!S.getLangOpts().HLSLStrictAvailability || 179*0fca6ea1SDimitry Andric (DeclEnv != nullptr && 180*0fca6ea1SDimitry Andric S.getASTContext().getTargetInfo().getTriple().getEnvironment() == 181*0fca6ea1SDimitry Andric llvm::Triple::EnvironmentType::Library)) 182*0fca6ea1SDimitry Andric return false; 183*0fca6ea1SDimitry Andric } 184*0fca6ea1SDimitry Andric 1855ffd83dbSDimitry Andric // Checks if we should emit the availability diagnostic in the context of C. 1865ffd83dbSDimitry Andric auto CheckContext = [&](const Decl *C) { 1875ffd83dbSDimitry Andric if (K == AR_NotYetIntroduced) { 1885ffd83dbSDimitry Andric if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C)) 189*0fca6ea1SDimitry Andric if (AA->getIntroduced() >= DeclVersion && 190*0fca6ea1SDimitry Andric AA->getEnvironment() == DeclEnv) 1915ffd83dbSDimitry Andric return true; 1925ffd83dbSDimitry Andric } else if (K == AR_Deprecated) { 1935ffd83dbSDimitry Andric if (C->isDeprecated()) 1945ffd83dbSDimitry Andric return true; 1955ffd83dbSDimitry Andric } else if (K == AR_Unavailable) { 1965ffd83dbSDimitry Andric // It is perfectly fine to refer to an 'unavailable' Objective-C method 1975ffd83dbSDimitry Andric // when it is referenced from within the @implementation itself. In this 1985ffd83dbSDimitry Andric // context, we interpret unavailable as a form of access control. 1995ffd83dbSDimitry Andric if (const auto *MD = dyn_cast<ObjCMethodDecl>(OffendingDecl)) { 2005ffd83dbSDimitry Andric if (const auto *Impl = dyn_cast<ObjCImplDecl>(C)) { 2015ffd83dbSDimitry Andric if (MD->getClassInterface() == Impl->getClassInterface()) 2025ffd83dbSDimitry Andric return true; 2035ffd83dbSDimitry Andric } 2045ffd83dbSDimitry Andric } 2055ffd83dbSDimitry Andric } 2065ffd83dbSDimitry Andric 2075ffd83dbSDimitry Andric if (C->isUnavailable()) 2085ffd83dbSDimitry Andric return true; 2095ffd83dbSDimitry Andric return false; 2105ffd83dbSDimitry Andric }; 2115ffd83dbSDimitry Andric 2125ffd83dbSDimitry Andric do { 2135ffd83dbSDimitry Andric if (CheckContext(Ctx)) 2145ffd83dbSDimitry Andric return false; 2155ffd83dbSDimitry Andric 2165ffd83dbSDimitry Andric // An implementation implicitly has the availability of the interface. 2175ffd83dbSDimitry Andric // Unless it is "+load" method. 2185ffd83dbSDimitry Andric if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Ctx)) 2195ffd83dbSDimitry Andric if (MethodD->isClassMethod() && 2205ffd83dbSDimitry Andric MethodD->getSelector().getAsString() == "load") 2215ffd83dbSDimitry Andric return true; 2225ffd83dbSDimitry Andric 2235ffd83dbSDimitry Andric if (const auto *CatOrImpl = dyn_cast<ObjCImplDecl>(Ctx)) { 2245ffd83dbSDimitry Andric if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface()) 2255ffd83dbSDimitry Andric if (CheckContext(Interface)) 2265ffd83dbSDimitry Andric return false; 2275ffd83dbSDimitry Andric } 2285ffd83dbSDimitry Andric // A category implicitly has the availability of the interface. 2295ffd83dbSDimitry Andric else if (const auto *CatD = dyn_cast<ObjCCategoryDecl>(Ctx)) 2305ffd83dbSDimitry Andric if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface()) 2315ffd83dbSDimitry Andric if (CheckContext(Interface)) 2325ffd83dbSDimitry Andric return false; 2335ffd83dbSDimitry Andric } while ((Ctx = cast_or_null<Decl>(Ctx->getDeclContext()))); 2345ffd83dbSDimitry Andric 2355ffd83dbSDimitry Andric return true; 2365ffd83dbSDimitry Andric } 2375ffd83dbSDimitry Andric 238*0fca6ea1SDimitry Andric static unsigned getAvailabilityDiagnosticKind( 239*0fca6ea1SDimitry Andric const ASTContext &Context, const VersionTuple &DeploymentVersion, 240*0fca6ea1SDimitry Andric const VersionTuple &DeclVersion, bool HasMatchingEnv) { 2415ffd83dbSDimitry Andric const auto &Triple = Context.getTargetInfo().getTriple(); 2425ffd83dbSDimitry Andric VersionTuple ForceAvailabilityFromVersion; 2435ffd83dbSDimitry Andric switch (Triple.getOS()) { 244*0fca6ea1SDimitry Andric // For iOS, emit the diagnostic even if -Wunguarded-availability is 245*0fca6ea1SDimitry Andric // not specified for deployment targets >= to iOS 11 or equivalent or 246*0fca6ea1SDimitry Andric // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or 247*0fca6ea1SDimitry Andric // later. 2485ffd83dbSDimitry Andric case llvm::Triple::IOS: 2495ffd83dbSDimitry Andric case llvm::Triple::TvOS: 2505ffd83dbSDimitry Andric ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11); 2515ffd83dbSDimitry Andric break; 2525ffd83dbSDimitry Andric case llvm::Triple::WatchOS: 2535ffd83dbSDimitry Andric ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4); 2545ffd83dbSDimitry Andric break; 2555ffd83dbSDimitry Andric case llvm::Triple::Darwin: 2565ffd83dbSDimitry Andric case llvm::Triple::MacOSX: 2575ffd83dbSDimitry Andric ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13); 2585ffd83dbSDimitry Andric break; 259*0fca6ea1SDimitry Andric // For HLSL, use diagnostic from HLSLAvailability group which 260*0fca6ea1SDimitry Andric // are reported as errors by default and in strict diagnostic mode 261*0fca6ea1SDimitry Andric // (-fhlsl-strict-availability) and as warnings in relaxed diagnostic 262*0fca6ea1SDimitry Andric // mode (-Wno-error=hlsl-availability) 263bdd1243dSDimitry Andric case llvm::Triple::ShaderModel: 264*0fca6ea1SDimitry Andric return HasMatchingEnv ? diag::warn_hlsl_availability 265*0fca6ea1SDimitry Andric : diag::warn_hlsl_availability_unavailable; 2665ffd83dbSDimitry Andric default: 267*0fca6ea1SDimitry Andric // New Apple targets should always warn about availability. 268*0fca6ea1SDimitry Andric ForceAvailabilityFromVersion = 269*0fca6ea1SDimitry Andric (Triple.getVendor() == llvm::Triple::Apple) 270*0fca6ea1SDimitry Andric ? VersionTuple(/*Major=*/0, 0) 271*0fca6ea1SDimitry Andric : VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1); 2725ffd83dbSDimitry Andric } 273*0fca6ea1SDimitry Andric if (DeploymentVersion >= ForceAvailabilityFromVersion || 274*0fca6ea1SDimitry Andric DeclVersion >= ForceAvailabilityFromVersion) 275*0fca6ea1SDimitry Andric return HasMatchingEnv ? diag::warn_unguarded_availability_new 276*0fca6ea1SDimitry Andric : diag::warn_unguarded_availability_unavailable_new; 277*0fca6ea1SDimitry Andric return HasMatchingEnv ? diag::warn_unguarded_availability 278*0fca6ea1SDimitry Andric : diag::warn_unguarded_availability_unavailable; 2795ffd83dbSDimitry Andric } 2805ffd83dbSDimitry Andric 2815ffd83dbSDimitry Andric static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) { 2825ffd83dbSDimitry Andric for (Decl *Ctx = OrigCtx; Ctx; 2835ffd83dbSDimitry Andric Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) { 2845ffd83dbSDimitry Andric if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx)) 2855ffd83dbSDimitry Andric return cast<NamedDecl>(Ctx); 2865ffd83dbSDimitry Andric if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) { 2875ffd83dbSDimitry Andric if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx)) 2885ffd83dbSDimitry Andric return Imp->getClassInterface(); 2895ffd83dbSDimitry Andric return CD; 2905ffd83dbSDimitry Andric } 2915ffd83dbSDimitry Andric } 2925ffd83dbSDimitry Andric 2935ffd83dbSDimitry Andric return dyn_cast<NamedDecl>(OrigCtx); 2945ffd83dbSDimitry Andric } 2955ffd83dbSDimitry Andric 2965ffd83dbSDimitry Andric namespace { 2975ffd83dbSDimitry Andric 2985ffd83dbSDimitry Andric struct AttributeInsertion { 2995ffd83dbSDimitry Andric StringRef Prefix; 3005ffd83dbSDimitry Andric SourceLocation Loc; 3015ffd83dbSDimitry Andric StringRef Suffix; 3025ffd83dbSDimitry Andric 3035ffd83dbSDimitry Andric static AttributeInsertion createInsertionAfter(const NamedDecl *D) { 3045ffd83dbSDimitry Andric return {" ", D->getEndLoc(), ""}; 3055ffd83dbSDimitry Andric } 3065ffd83dbSDimitry Andric static AttributeInsertion createInsertionAfter(SourceLocation Loc) { 3075ffd83dbSDimitry Andric return {" ", Loc, ""}; 3085ffd83dbSDimitry Andric } 3095ffd83dbSDimitry Andric static AttributeInsertion createInsertionBefore(const NamedDecl *D) { 3105ffd83dbSDimitry Andric return {"", D->getBeginLoc(), "\n"}; 3115ffd83dbSDimitry Andric } 3125ffd83dbSDimitry Andric }; 3135ffd83dbSDimitry Andric 3145ffd83dbSDimitry Andric } // end anonymous namespace 3155ffd83dbSDimitry Andric 3165ffd83dbSDimitry Andric /// Tries to parse a string as ObjC method name. 3175ffd83dbSDimitry Andric /// 3185ffd83dbSDimitry Andric /// \param Name The string to parse. Expected to originate from availability 3195ffd83dbSDimitry Andric /// attribute argument. 3205ffd83dbSDimitry Andric /// \param SlotNames The vector that will be populated with slot names. In case 3215ffd83dbSDimitry Andric /// of unsuccessful parsing can contain invalid data. 322bdd1243dSDimitry Andric /// \returns A number of method parameters if parsing was successful, 323bdd1243dSDimitry Andric /// std::nullopt otherwise. 324bdd1243dSDimitry Andric static std::optional<unsigned> 3255ffd83dbSDimitry Andric tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &SlotNames, 3265ffd83dbSDimitry Andric const LangOptions &LangOpts) { 3275ffd83dbSDimitry Andric // Accept replacements starting with - or + as valid ObjC method names. 3285ffd83dbSDimitry Andric if (!Name.empty() && (Name.front() == '-' || Name.front() == '+')) 3295ffd83dbSDimitry Andric Name = Name.drop_front(1); 3305ffd83dbSDimitry Andric if (Name.empty()) 331bdd1243dSDimitry Andric return std::nullopt; 3325ffd83dbSDimitry Andric Name.split(SlotNames, ':'); 3335ffd83dbSDimitry Andric unsigned NumParams; 3345ffd83dbSDimitry Andric if (Name.back() == ':') { 3355ffd83dbSDimitry Andric // Remove an empty string at the end that doesn't represent any slot. 3365ffd83dbSDimitry Andric SlotNames.pop_back(); 3375ffd83dbSDimitry Andric NumParams = SlotNames.size(); 3385ffd83dbSDimitry Andric } else { 3395ffd83dbSDimitry Andric if (SlotNames.size() != 1) 3405ffd83dbSDimitry Andric // Not a valid method name, just a colon-separated string. 341bdd1243dSDimitry Andric return std::nullopt; 3425ffd83dbSDimitry Andric NumParams = 0; 3435ffd83dbSDimitry Andric } 3445ffd83dbSDimitry Andric // Verify all slot names are valid. 3455ffd83dbSDimitry Andric bool AllowDollar = LangOpts.DollarIdents; 3465ffd83dbSDimitry Andric for (StringRef S : SlotNames) { 3475ffd83dbSDimitry Andric if (S.empty()) 3485ffd83dbSDimitry Andric continue; 349349cc55cSDimitry Andric if (!isValidAsciiIdentifier(S, AllowDollar)) 350bdd1243dSDimitry Andric return std::nullopt; 3515ffd83dbSDimitry Andric } 3525ffd83dbSDimitry Andric return NumParams; 3535ffd83dbSDimitry Andric } 3545ffd83dbSDimitry Andric 3555ffd83dbSDimitry Andric /// Returns a source location in which it's appropriate to insert a new 3565ffd83dbSDimitry Andric /// attribute for the given declaration \D. 357bdd1243dSDimitry Andric static std::optional<AttributeInsertion> 3585ffd83dbSDimitry Andric createAttributeInsertion(const NamedDecl *D, const SourceManager &SM, 3595ffd83dbSDimitry Andric const LangOptions &LangOpts) { 3605ffd83dbSDimitry Andric if (isa<ObjCPropertyDecl>(D)) 3615ffd83dbSDimitry Andric return AttributeInsertion::createInsertionAfter(D); 3625ffd83dbSDimitry Andric if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { 3635ffd83dbSDimitry Andric if (MD->hasBody()) 364bdd1243dSDimitry Andric return std::nullopt; 3655ffd83dbSDimitry Andric return AttributeInsertion::createInsertionAfter(D); 3665ffd83dbSDimitry Andric } 3675ffd83dbSDimitry Andric if (const auto *TD = dyn_cast<TagDecl>(D)) { 3685ffd83dbSDimitry Andric SourceLocation Loc = 3695ffd83dbSDimitry Andric Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 0, SM, LangOpts); 3705ffd83dbSDimitry Andric if (Loc.isInvalid()) 371bdd1243dSDimitry Andric return std::nullopt; 3725ffd83dbSDimitry Andric // Insert after the 'struct'/whatever keyword. 3735ffd83dbSDimitry Andric return AttributeInsertion::createInsertionAfter(Loc); 3745ffd83dbSDimitry Andric } 3755ffd83dbSDimitry Andric return AttributeInsertion::createInsertionBefore(D); 3765ffd83dbSDimitry Andric } 3775ffd83dbSDimitry Andric 3785ffd83dbSDimitry Andric /// Actually emit an availability diagnostic for a reference to an unavailable 3795ffd83dbSDimitry Andric /// decl. 3805ffd83dbSDimitry Andric /// 3815ffd83dbSDimitry Andric /// \param Ctx The context that the reference occurred in 3825ffd83dbSDimitry Andric /// \param ReferringDecl The exact declaration that was referenced. 3835ffd83dbSDimitry Andric /// \param OffendingDecl A related decl to \c ReferringDecl that has an 3845ffd83dbSDimitry Andric /// availability attribute corresponding to \c K attached to it. Note that this 3855ffd83dbSDimitry Andric /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and 3865ffd83dbSDimitry Andric /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl 3875ffd83dbSDimitry Andric /// and OffendingDecl is the EnumDecl. 3885ffd83dbSDimitry Andric static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, 3895ffd83dbSDimitry Andric Decl *Ctx, const NamedDecl *ReferringDecl, 3905ffd83dbSDimitry Andric const NamedDecl *OffendingDecl, 3915ffd83dbSDimitry Andric StringRef Message, 3925ffd83dbSDimitry Andric ArrayRef<SourceLocation> Locs, 3935ffd83dbSDimitry Andric const ObjCInterfaceDecl *UnknownObjCClass, 3945ffd83dbSDimitry Andric const ObjCPropertyDecl *ObjCProperty, 3955ffd83dbSDimitry Andric bool ObjCPropertyAccess) { 3965ffd83dbSDimitry Andric // Diagnostics for deprecated or unavailable. 3975ffd83dbSDimitry Andric unsigned diag, diag_message, diag_fwdclass_message; 3985ffd83dbSDimitry Andric unsigned diag_available_here = diag::note_availability_specified_here; 3995ffd83dbSDimitry Andric SourceLocation NoteLocation = OffendingDecl->getLocation(); 4005ffd83dbSDimitry Andric 4015ffd83dbSDimitry Andric // Matches 'diag::note_property_attribute' options. 4025ffd83dbSDimitry Andric unsigned property_note_select; 4035ffd83dbSDimitry Andric 4045ffd83dbSDimitry Andric // Matches diag::note_availability_specified_here. 4055ffd83dbSDimitry Andric unsigned available_here_select_kind; 4065ffd83dbSDimitry Andric 4075ffd83dbSDimitry Andric VersionTuple DeclVersion; 408*0fca6ea1SDimitry Andric const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl); 409*0fca6ea1SDimitry Andric const IdentifierInfo *IIEnv = nullptr; 410*0fca6ea1SDimitry Andric if (AA) { 4115ffd83dbSDimitry Andric DeclVersion = AA->getIntroduced(); 412*0fca6ea1SDimitry Andric IIEnv = AA->getEnvironment(); 413*0fca6ea1SDimitry Andric } 4145ffd83dbSDimitry Andric 415*0fca6ea1SDimitry Andric if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, IIEnv, Ctx, 4165ffd83dbSDimitry Andric OffendingDecl)) 4175ffd83dbSDimitry Andric return; 4185ffd83dbSDimitry Andric 4195ffd83dbSDimitry Andric SourceLocation Loc = Locs.front(); 4205ffd83dbSDimitry Andric 4215ffd83dbSDimitry Andric // The declaration can have multiple availability attributes, we are looking 4225ffd83dbSDimitry Andric // at one of them. 423*0fca6ea1SDimitry Andric if (AA && AA->isInherited()) { 4245ffd83dbSDimitry Andric for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl; 4255ffd83dbSDimitry Andric Redecl = Redecl->getPreviousDecl()) { 4265ffd83dbSDimitry Andric const AvailabilityAttr *AForRedecl = 4275ffd83dbSDimitry Andric getAttrForPlatform(S.Context, Redecl); 4285ffd83dbSDimitry Andric if (AForRedecl && !AForRedecl->isInherited()) { 4295ffd83dbSDimitry Andric // If D is a declaration with inherited attributes, the note should 4305ffd83dbSDimitry Andric // point to the declaration with actual attributes. 4315ffd83dbSDimitry Andric NoteLocation = Redecl->getLocation(); 4325ffd83dbSDimitry Andric break; 4335ffd83dbSDimitry Andric } 4345ffd83dbSDimitry Andric } 4355ffd83dbSDimitry Andric } 4365ffd83dbSDimitry Andric 4375ffd83dbSDimitry Andric switch (K) { 4385ffd83dbSDimitry Andric case AR_NotYetIntroduced: { 4395ffd83dbSDimitry Andric // We would like to emit the diagnostic even if -Wunguarded-availability is 4405ffd83dbSDimitry Andric // not specified for deployment targets >= to iOS 11 or equivalent or 4415ffd83dbSDimitry Andric // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or 4425ffd83dbSDimitry Andric // later. 443*0fca6ea1SDimitry Andric assert(AA != nullptr && "expecting valid availability attribute"); 4445ffd83dbSDimitry Andric VersionTuple Introduced = AA->getIntroduced(); 445*0fca6ea1SDimitry Andric bool EnvironmentMatchesOrNone = 446*0fca6ea1SDimitry Andric hasMatchingEnvironmentOrNone(S.getASTContext(), AA); 4475ffd83dbSDimitry Andric 448*0fca6ea1SDimitry Andric const TargetInfo &TI = S.getASTContext().getTargetInfo(); 449*0fca6ea1SDimitry Andric std::string PlatformName( 450*0fca6ea1SDimitry Andric AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); 451*0fca6ea1SDimitry Andric llvm::StringRef TargetEnvironment( 452*0fca6ea1SDimitry Andric llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment())); 453*0fca6ea1SDimitry Andric llvm::StringRef AttrEnvironment = 454*0fca6ea1SDimitry Andric AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; 455*0fca6ea1SDimitry Andric bool UseEnvironment = 456*0fca6ea1SDimitry Andric (!AttrEnvironment.empty() && !TargetEnvironment.empty()); 457*0fca6ea1SDimitry Andric 458*0fca6ea1SDimitry Andric unsigned DiagKind = getAvailabilityDiagnosticKind( 4595ffd83dbSDimitry Andric S.Context, S.Context.getTargetInfo().getPlatformMinVersion(), 460*0fca6ea1SDimitry Andric Introduced, EnvironmentMatchesOrNone); 4615ffd83dbSDimitry Andric 462*0fca6ea1SDimitry Andric S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName 463*0fca6ea1SDimitry Andric << Introduced.getAsString() << UseEnvironment 464*0fca6ea1SDimitry Andric << TargetEnvironment; 4655ffd83dbSDimitry Andric 4665ffd83dbSDimitry Andric S.Diag(OffendingDecl->getLocation(), 4675ffd83dbSDimitry Andric diag::note_partial_availability_specified_here) 4685ffd83dbSDimitry Andric << OffendingDecl << PlatformName << Introduced.getAsString() 469*0fca6ea1SDimitry Andric << S.Context.getTargetInfo().getPlatformMinVersion().getAsString() 470*0fca6ea1SDimitry Andric << UseEnvironment << AttrEnvironment << TargetEnvironment; 471*0fca6ea1SDimitry Andric 472*0fca6ea1SDimitry Andric // Do not offer to silence the warning or fixits for HLSL 473*0fca6ea1SDimitry Andric if (S.getLangOpts().HLSL) 474*0fca6ea1SDimitry Andric return; 4755ffd83dbSDimitry Andric 4765ffd83dbSDimitry Andric if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) { 4775ffd83dbSDimitry Andric if (const auto *TD = dyn_cast<TagDecl>(Enclosing)) 4785ffd83dbSDimitry Andric if (TD->getDeclName().isEmpty()) { 4795ffd83dbSDimitry Andric S.Diag(TD->getLocation(), 4805ffd83dbSDimitry Andric diag::note_decl_unguarded_availability_silence) 4815ffd83dbSDimitry Andric << /*Anonymous*/ 1 << TD->getKindName(); 4825ffd83dbSDimitry Andric return; 4835ffd83dbSDimitry Andric } 4845ffd83dbSDimitry Andric auto FixitNoteDiag = 4855ffd83dbSDimitry Andric S.Diag(Enclosing->getLocation(), 4865ffd83dbSDimitry Andric diag::note_decl_unguarded_availability_silence) 4875ffd83dbSDimitry Andric << /*Named*/ 0 << Enclosing; 4885ffd83dbSDimitry Andric // Don't offer a fixit for declarations with availability attributes. 4895ffd83dbSDimitry Andric if (Enclosing->hasAttr<AvailabilityAttr>()) 4905ffd83dbSDimitry Andric return; 4915ffd83dbSDimitry Andric if (!S.getPreprocessor().isMacroDefined("API_AVAILABLE")) 4925ffd83dbSDimitry Andric return; 493bdd1243dSDimitry Andric std::optional<AttributeInsertion> Insertion = createAttributeInsertion( 4945ffd83dbSDimitry Andric Enclosing, S.getSourceManager(), S.getLangOpts()); 4955ffd83dbSDimitry Andric if (!Insertion) 4965ffd83dbSDimitry Andric return; 4975ffd83dbSDimitry Andric std::string PlatformName = 4985ffd83dbSDimitry Andric AvailabilityAttr::getPlatformNameSourceSpelling( 4995ffd83dbSDimitry Andric S.getASTContext().getTargetInfo().getPlatformName()) 5005ffd83dbSDimitry Andric .lower(); 5015ffd83dbSDimitry Andric std::string Introduced = 5025ffd83dbSDimitry Andric OffendingDecl->getVersionIntroduced().getAsString(); 5035ffd83dbSDimitry Andric FixitNoteDiag << FixItHint::CreateInsertion( 5045ffd83dbSDimitry Andric Insertion->Loc, 5055ffd83dbSDimitry Andric (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" + PlatformName + 5065ffd83dbSDimitry Andric "(" + Introduced + "))" + Insertion->Suffix) 5075ffd83dbSDimitry Andric .str()); 5085ffd83dbSDimitry Andric } 5095ffd83dbSDimitry Andric return; 5105ffd83dbSDimitry Andric } 5115ffd83dbSDimitry Andric case AR_Deprecated: 5125ffd83dbSDimitry Andric diag = !ObjCPropertyAccess ? diag::warn_deprecated 5135ffd83dbSDimitry Andric : diag::warn_property_method_deprecated; 5145ffd83dbSDimitry Andric diag_message = diag::warn_deprecated_message; 5155ffd83dbSDimitry Andric diag_fwdclass_message = diag::warn_deprecated_fwdclass_message; 5165ffd83dbSDimitry Andric property_note_select = /* deprecated */ 0; 5175ffd83dbSDimitry Andric available_here_select_kind = /* deprecated */ 2; 5185ffd83dbSDimitry Andric if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>()) 5195ffd83dbSDimitry Andric NoteLocation = AL->getLocation(); 5205ffd83dbSDimitry Andric break; 5215ffd83dbSDimitry Andric 5225ffd83dbSDimitry Andric case AR_Unavailable: 5235ffd83dbSDimitry Andric diag = !ObjCPropertyAccess ? diag::err_unavailable 5245ffd83dbSDimitry Andric : diag::err_property_method_unavailable; 5255ffd83dbSDimitry Andric diag_message = diag::err_unavailable_message; 5265ffd83dbSDimitry Andric diag_fwdclass_message = diag::warn_unavailable_fwdclass_message; 5275ffd83dbSDimitry Andric property_note_select = /* unavailable */ 1; 5285ffd83dbSDimitry Andric available_here_select_kind = /* unavailable */ 0; 5295ffd83dbSDimitry Andric 5305ffd83dbSDimitry Andric if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) { 5315ffd83dbSDimitry Andric if (AL->isImplicit() && AL->getImplicitReason()) { 5325ffd83dbSDimitry Andric // Most of these failures are due to extra restrictions in ARC; 5335ffd83dbSDimitry Andric // reflect that in the primary diagnostic when applicable. 5345ffd83dbSDimitry Andric auto flagARCError = [&] { 5355ffd83dbSDimitry Andric if (S.getLangOpts().ObjCAutoRefCount && 5365ffd83dbSDimitry Andric S.getSourceManager().isInSystemHeader( 5375ffd83dbSDimitry Andric OffendingDecl->getLocation())) 5385ffd83dbSDimitry Andric diag = diag::err_unavailable_in_arc; 5395ffd83dbSDimitry Andric }; 5405ffd83dbSDimitry Andric 5415ffd83dbSDimitry Andric switch (AL->getImplicitReason()) { 5425ffd83dbSDimitry Andric case UnavailableAttr::IR_None: break; 5435ffd83dbSDimitry Andric 5445ffd83dbSDimitry Andric case UnavailableAttr::IR_ARCForbiddenType: 5455ffd83dbSDimitry Andric flagARCError(); 5465ffd83dbSDimitry Andric diag_available_here = diag::note_arc_forbidden_type; 5475ffd83dbSDimitry Andric break; 5485ffd83dbSDimitry Andric 5495ffd83dbSDimitry Andric case UnavailableAttr::IR_ForbiddenWeak: 5505ffd83dbSDimitry Andric if (S.getLangOpts().ObjCWeakRuntime) 5515ffd83dbSDimitry Andric diag_available_here = diag::note_arc_weak_disabled; 5525ffd83dbSDimitry Andric else 5535ffd83dbSDimitry Andric diag_available_here = diag::note_arc_weak_no_runtime; 5545ffd83dbSDimitry Andric break; 5555ffd83dbSDimitry Andric 5565ffd83dbSDimitry Andric case UnavailableAttr::IR_ARCForbiddenConversion: 5575ffd83dbSDimitry Andric flagARCError(); 5585ffd83dbSDimitry Andric diag_available_here = diag::note_performs_forbidden_arc_conversion; 5595ffd83dbSDimitry Andric break; 5605ffd83dbSDimitry Andric 5615ffd83dbSDimitry Andric case UnavailableAttr::IR_ARCInitReturnsUnrelated: 5625ffd83dbSDimitry Andric flagARCError(); 5635ffd83dbSDimitry Andric diag_available_here = diag::note_arc_init_returns_unrelated; 5645ffd83dbSDimitry Andric break; 5655ffd83dbSDimitry Andric 5665ffd83dbSDimitry Andric case UnavailableAttr::IR_ARCFieldWithOwnership: 5675ffd83dbSDimitry Andric flagARCError(); 5685ffd83dbSDimitry Andric diag_available_here = diag::note_arc_field_with_ownership; 5695ffd83dbSDimitry Andric break; 5705ffd83dbSDimitry Andric } 5715ffd83dbSDimitry Andric } 5725ffd83dbSDimitry Andric } 5735ffd83dbSDimitry Andric break; 5745ffd83dbSDimitry Andric 5755ffd83dbSDimitry Andric case AR_Available: 5765ffd83dbSDimitry Andric llvm_unreachable("Warning for availability of available declaration?"); 5775ffd83dbSDimitry Andric } 5785ffd83dbSDimitry Andric 5795ffd83dbSDimitry Andric SmallVector<FixItHint, 12> FixIts; 5805ffd83dbSDimitry Andric if (K == AR_Deprecated) { 5815ffd83dbSDimitry Andric StringRef Replacement; 5825ffd83dbSDimitry Andric if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>()) 5835ffd83dbSDimitry Andric Replacement = AL->getReplacement(); 5845ffd83dbSDimitry Andric if (auto AL = getAttrForPlatform(S.Context, OffendingDecl)) 5855ffd83dbSDimitry Andric Replacement = AL->getReplacement(); 5865ffd83dbSDimitry Andric 5875ffd83dbSDimitry Andric CharSourceRange UseRange; 5885ffd83dbSDimitry Andric if (!Replacement.empty()) 5895ffd83dbSDimitry Andric UseRange = 5905ffd83dbSDimitry Andric CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); 5915ffd83dbSDimitry Andric if (UseRange.isValid()) { 5925ffd83dbSDimitry Andric if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(ReferringDecl)) { 5935ffd83dbSDimitry Andric Selector Sel = MethodDecl->getSelector(); 5945ffd83dbSDimitry Andric SmallVector<StringRef, 12> SelectorSlotNames; 595bdd1243dSDimitry Andric std::optional<unsigned> NumParams = tryParseObjCMethodName( 5965ffd83dbSDimitry Andric Replacement, SelectorSlotNames, S.getLangOpts()); 59781ad6265SDimitry Andric if (NumParams && *NumParams == Sel.getNumArgs()) { 5985ffd83dbSDimitry Andric assert(SelectorSlotNames.size() == Locs.size()); 5995ffd83dbSDimitry Andric for (unsigned I = 0; I < Locs.size(); ++I) { 6005ffd83dbSDimitry Andric if (!Sel.getNameForSlot(I).empty()) { 6015ffd83dbSDimitry Andric CharSourceRange NameRange = CharSourceRange::getCharRange( 6025ffd83dbSDimitry Andric Locs[I], S.getLocForEndOfToken(Locs[I])); 6035ffd83dbSDimitry Andric FixIts.push_back(FixItHint::CreateReplacement( 6045ffd83dbSDimitry Andric NameRange, SelectorSlotNames[I])); 6055ffd83dbSDimitry Andric } else 6065ffd83dbSDimitry Andric FixIts.push_back( 6075ffd83dbSDimitry Andric FixItHint::CreateInsertion(Locs[I], SelectorSlotNames[I])); 6085ffd83dbSDimitry Andric } 6095ffd83dbSDimitry Andric } else 6105ffd83dbSDimitry Andric FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement)); 6115ffd83dbSDimitry Andric } else 6125ffd83dbSDimitry Andric FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement)); 6135ffd83dbSDimitry Andric } 6145ffd83dbSDimitry Andric } 6155ffd83dbSDimitry Andric 6165f757f3fSDimitry Andric // We emit deprecation warning for deprecated specializations 6175f757f3fSDimitry Andric // when their instantiation stacks originate outside 6185f757f3fSDimitry Andric // of a system header, even if the diagnostics is suppresed at the 6195f757f3fSDimitry Andric // point of definition. 6205f757f3fSDimitry Andric SourceLocation InstantiationLoc = 6215f757f3fSDimitry Andric S.getTopMostPointOfInstantiation(ReferringDecl); 6225f757f3fSDimitry Andric bool ShouldAllowWarningInSystemHeader = 6235f757f3fSDimitry Andric InstantiationLoc != Loc && 6245f757f3fSDimitry Andric !S.getSourceManager().isInSystemHeader(InstantiationLoc); 6255f757f3fSDimitry Andric struct AllowWarningInSystemHeaders { 6265f757f3fSDimitry Andric AllowWarningInSystemHeaders(DiagnosticsEngine &E, 6275f757f3fSDimitry Andric bool AllowWarningInSystemHeaders) 6285f757f3fSDimitry Andric : Engine(E), Prev(E.getSuppressSystemWarnings()) { 6295f757f3fSDimitry Andric E.setSuppressSystemWarnings(!AllowWarningInSystemHeaders); 6305f757f3fSDimitry Andric } 6315f757f3fSDimitry Andric ~AllowWarningInSystemHeaders() { Engine.setSuppressSystemWarnings(Prev); } 6325f757f3fSDimitry Andric 6335f757f3fSDimitry Andric private: 6345f757f3fSDimitry Andric DiagnosticsEngine &Engine; 6355f757f3fSDimitry Andric bool Prev; 6365f757f3fSDimitry Andric } SystemWarningOverrideRAII(S.getDiagnostics(), 6375f757f3fSDimitry Andric ShouldAllowWarningInSystemHeader); 6385f757f3fSDimitry Andric 6395ffd83dbSDimitry Andric if (!Message.empty()) { 6405ffd83dbSDimitry Andric S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts; 6415ffd83dbSDimitry Andric if (ObjCProperty) 6425ffd83dbSDimitry Andric S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) 6435ffd83dbSDimitry Andric << ObjCProperty->getDeclName() << property_note_select; 6445ffd83dbSDimitry Andric } else if (!UnknownObjCClass) { 6455ffd83dbSDimitry Andric S.Diag(Loc, diag) << ReferringDecl << FixIts; 6465ffd83dbSDimitry Andric if (ObjCProperty) 6475ffd83dbSDimitry Andric S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) 6485ffd83dbSDimitry Andric << ObjCProperty->getDeclName() << property_note_select; 6495ffd83dbSDimitry Andric } else { 6505ffd83dbSDimitry Andric S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts; 6515ffd83dbSDimitry Andric S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); 6525ffd83dbSDimitry Andric } 6535ffd83dbSDimitry Andric 6545ffd83dbSDimitry Andric S.Diag(NoteLocation, diag_available_here) 6555ffd83dbSDimitry Andric << OffendingDecl << available_here_select_kind; 6565ffd83dbSDimitry Andric } 6575ffd83dbSDimitry Andric 6585ffd83dbSDimitry Andric void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) { 6595ffd83dbSDimitry Andric assert(DD.Kind == DelayedDiagnostic::Availability && 6605ffd83dbSDimitry Andric "Expected an availability diagnostic here"); 6615ffd83dbSDimitry Andric 6625ffd83dbSDimitry Andric DD.Triggered = true; 6635ffd83dbSDimitry Andric DoEmitAvailabilityWarning( 6645ffd83dbSDimitry Andric *this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(), 6655ffd83dbSDimitry Andric DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(), 6665ffd83dbSDimitry Andric DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(), 6675ffd83dbSDimitry Andric DD.getObjCProperty(), false); 6685ffd83dbSDimitry Andric } 6695ffd83dbSDimitry Andric 6705ffd83dbSDimitry Andric static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR, 6715ffd83dbSDimitry Andric const NamedDecl *ReferringDecl, 6725ffd83dbSDimitry Andric const NamedDecl *OffendingDecl, 6735ffd83dbSDimitry Andric StringRef Message, 6745ffd83dbSDimitry Andric ArrayRef<SourceLocation> Locs, 6755ffd83dbSDimitry Andric const ObjCInterfaceDecl *UnknownObjCClass, 6765ffd83dbSDimitry Andric const ObjCPropertyDecl *ObjCProperty, 6775ffd83dbSDimitry Andric bool ObjCPropertyAccess) { 6785ffd83dbSDimitry Andric // Delay if we're currently parsing a declaration. 6795ffd83dbSDimitry Andric if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { 6805ffd83dbSDimitry Andric S.DelayedDiagnostics.add( 6815ffd83dbSDimitry Andric DelayedDiagnostic::makeAvailability( 6825ffd83dbSDimitry Andric AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass, 6835ffd83dbSDimitry Andric ObjCProperty, Message, ObjCPropertyAccess)); 6845ffd83dbSDimitry Andric return; 6855ffd83dbSDimitry Andric } 6865ffd83dbSDimitry Andric 6875ffd83dbSDimitry Andric Decl *Ctx = cast<Decl>(S.getCurLexicalContext()); 6885ffd83dbSDimitry Andric DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl, 6895ffd83dbSDimitry Andric Message, Locs, UnknownObjCClass, ObjCProperty, 6905ffd83dbSDimitry Andric ObjCPropertyAccess); 6915ffd83dbSDimitry Andric } 6925ffd83dbSDimitry Andric 6935ffd83dbSDimitry Andric namespace { 6945ffd83dbSDimitry Andric 6955ffd83dbSDimitry Andric /// Returns true if the given statement can be a body-like child of \p Parent. 6965ffd83dbSDimitry Andric bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) { 6975ffd83dbSDimitry Andric switch (Parent->getStmtClass()) { 6985ffd83dbSDimitry Andric case Stmt::IfStmtClass: 6995ffd83dbSDimitry Andric return cast<IfStmt>(Parent)->getThen() == S || 7005ffd83dbSDimitry Andric cast<IfStmt>(Parent)->getElse() == S; 7015ffd83dbSDimitry Andric case Stmt::WhileStmtClass: 7025ffd83dbSDimitry Andric return cast<WhileStmt>(Parent)->getBody() == S; 7035ffd83dbSDimitry Andric case Stmt::DoStmtClass: 7045ffd83dbSDimitry Andric return cast<DoStmt>(Parent)->getBody() == S; 7055ffd83dbSDimitry Andric case Stmt::ForStmtClass: 7065ffd83dbSDimitry Andric return cast<ForStmt>(Parent)->getBody() == S; 7075ffd83dbSDimitry Andric case Stmt::CXXForRangeStmtClass: 7085ffd83dbSDimitry Andric return cast<CXXForRangeStmt>(Parent)->getBody() == S; 7095ffd83dbSDimitry Andric case Stmt::ObjCForCollectionStmtClass: 7105ffd83dbSDimitry Andric return cast<ObjCForCollectionStmt>(Parent)->getBody() == S; 7115ffd83dbSDimitry Andric case Stmt::CaseStmtClass: 7125ffd83dbSDimitry Andric case Stmt::DefaultStmtClass: 7135ffd83dbSDimitry Andric return cast<SwitchCase>(Parent)->getSubStmt() == S; 7145ffd83dbSDimitry Andric default: 7155ffd83dbSDimitry Andric return false; 7165ffd83dbSDimitry Andric } 7175ffd83dbSDimitry Andric } 7185ffd83dbSDimitry Andric 7195ffd83dbSDimitry Andric class StmtUSEFinder : public RecursiveASTVisitor<StmtUSEFinder> { 7205ffd83dbSDimitry Andric const Stmt *Target; 7215ffd83dbSDimitry Andric 7225ffd83dbSDimitry Andric public: 7235ffd83dbSDimitry Andric bool VisitStmt(Stmt *S) { return S != Target; } 7245ffd83dbSDimitry Andric 7255ffd83dbSDimitry Andric /// Returns true if the given statement is present in the given declaration. 7265ffd83dbSDimitry Andric static bool isContained(const Stmt *Target, const Decl *D) { 7275ffd83dbSDimitry Andric StmtUSEFinder Visitor; 7285ffd83dbSDimitry Andric Visitor.Target = Target; 7295ffd83dbSDimitry Andric return !Visitor.TraverseDecl(const_cast<Decl *>(D)); 7305ffd83dbSDimitry Andric } 7315ffd83dbSDimitry Andric }; 7325ffd83dbSDimitry Andric 7335ffd83dbSDimitry Andric /// Traverses the AST and finds the last statement that used a given 7345ffd83dbSDimitry Andric /// declaration. 7355ffd83dbSDimitry Andric class LastDeclUSEFinder : public RecursiveASTVisitor<LastDeclUSEFinder> { 7365ffd83dbSDimitry Andric const Decl *D; 7375ffd83dbSDimitry Andric 7385ffd83dbSDimitry Andric public: 7395ffd83dbSDimitry Andric bool VisitDeclRefExpr(DeclRefExpr *DRE) { 7405ffd83dbSDimitry Andric if (DRE->getDecl() == D) 7415ffd83dbSDimitry Andric return false; 7425ffd83dbSDimitry Andric return true; 7435ffd83dbSDimitry Andric } 7445ffd83dbSDimitry Andric 7455ffd83dbSDimitry Andric static const Stmt *findLastStmtThatUsesDecl(const Decl *D, 7465ffd83dbSDimitry Andric const CompoundStmt *Scope) { 7475ffd83dbSDimitry Andric LastDeclUSEFinder Visitor; 7485ffd83dbSDimitry Andric Visitor.D = D; 749349cc55cSDimitry Andric for (const Stmt *S : llvm::reverse(Scope->body())) { 7505ffd83dbSDimitry Andric if (!Visitor.TraverseStmt(const_cast<Stmt *>(S))) 7515ffd83dbSDimitry Andric return S; 7525ffd83dbSDimitry Andric } 7535ffd83dbSDimitry Andric return nullptr; 7545ffd83dbSDimitry Andric } 7555ffd83dbSDimitry Andric }; 7565ffd83dbSDimitry Andric 7575ffd83dbSDimitry Andric /// This class implements -Wunguarded-availability. 7585ffd83dbSDimitry Andric /// 7595ffd83dbSDimitry Andric /// This is done with a traversal of the AST of a function that makes reference 7605ffd83dbSDimitry Andric /// to a partially available declaration. Whenever we encounter an \c if of the 7615ffd83dbSDimitry Andric /// form: \c if(@available(...)), we use the version from the condition to visit 7625ffd83dbSDimitry Andric /// the then statement. 7635ffd83dbSDimitry Andric class DiagnoseUnguardedAvailability 7645ffd83dbSDimitry Andric : public RecursiveASTVisitor<DiagnoseUnguardedAvailability> { 7655ffd83dbSDimitry Andric typedef RecursiveASTVisitor<DiagnoseUnguardedAvailability> Base; 7665ffd83dbSDimitry Andric 7675ffd83dbSDimitry Andric Sema &SemaRef; 7685ffd83dbSDimitry Andric Decl *Ctx; 7695ffd83dbSDimitry Andric 7705ffd83dbSDimitry Andric /// Stack of potentially nested 'if (@available(...))'s. 7715ffd83dbSDimitry Andric SmallVector<VersionTuple, 8> AvailabilityStack; 7725ffd83dbSDimitry Andric SmallVector<const Stmt *, 16> StmtStack; 7735ffd83dbSDimitry Andric 7745ffd83dbSDimitry Andric void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range, 7755ffd83dbSDimitry Andric ObjCInterfaceDecl *ClassReceiver = nullptr); 7765ffd83dbSDimitry Andric 7775ffd83dbSDimitry Andric public: 7785ffd83dbSDimitry Andric DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx) 7795ffd83dbSDimitry Andric : SemaRef(SemaRef), Ctx(Ctx) { 7805ffd83dbSDimitry Andric AvailabilityStack.push_back( 7815ffd83dbSDimitry Andric SemaRef.Context.getTargetInfo().getPlatformMinVersion()); 7825ffd83dbSDimitry Andric } 7835ffd83dbSDimitry Andric 7845ffd83dbSDimitry Andric bool TraverseStmt(Stmt *S) { 7855ffd83dbSDimitry Andric if (!S) 7865ffd83dbSDimitry Andric return true; 7875ffd83dbSDimitry Andric StmtStack.push_back(S); 7885ffd83dbSDimitry Andric bool Result = Base::TraverseStmt(S); 7895ffd83dbSDimitry Andric StmtStack.pop_back(); 7905ffd83dbSDimitry Andric return Result; 7915ffd83dbSDimitry Andric } 7925ffd83dbSDimitry Andric 7935ffd83dbSDimitry Andric void IssueDiagnostics(Stmt *S) { TraverseStmt(S); } 7945ffd83dbSDimitry Andric 7955ffd83dbSDimitry Andric bool TraverseIfStmt(IfStmt *If); 7965ffd83dbSDimitry Andric 7975ffd83dbSDimitry Andric // for 'case X:' statements, don't bother looking at the 'X'; it can't lead 7985ffd83dbSDimitry Andric // to any useful diagnostics. 7995ffd83dbSDimitry Andric bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); } 8005ffd83dbSDimitry Andric 801fe6060f1SDimitry Andric bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) { return true; } 8025ffd83dbSDimitry Andric 8035ffd83dbSDimitry Andric bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) { 8045ffd83dbSDimitry Andric if (ObjCMethodDecl *D = Msg->getMethodDecl()) { 8055ffd83dbSDimitry Andric ObjCInterfaceDecl *ID = nullptr; 8065ffd83dbSDimitry Andric QualType ReceiverTy = Msg->getClassReceiver(); 8075ffd83dbSDimitry Andric if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType()) 8085ffd83dbSDimitry Andric ID = ReceiverTy->getAsObjCInterfaceType()->getInterface(); 8095ffd83dbSDimitry Andric 8105ffd83dbSDimitry Andric DiagnoseDeclAvailability( 8115ffd83dbSDimitry Andric D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID); 8125ffd83dbSDimitry Andric } 8135ffd83dbSDimitry Andric return true; 8145ffd83dbSDimitry Andric } 8155ffd83dbSDimitry Andric 8165ffd83dbSDimitry Andric bool VisitDeclRefExpr(DeclRefExpr *DRE) { 8175ffd83dbSDimitry Andric DiagnoseDeclAvailability(DRE->getDecl(), 8185ffd83dbSDimitry Andric SourceRange(DRE->getBeginLoc(), DRE->getEndLoc())); 8195ffd83dbSDimitry Andric return true; 8205ffd83dbSDimitry Andric } 8215ffd83dbSDimitry Andric 8225ffd83dbSDimitry Andric bool VisitMemberExpr(MemberExpr *ME) { 8235ffd83dbSDimitry Andric DiagnoseDeclAvailability(ME->getMemberDecl(), 8245ffd83dbSDimitry Andric SourceRange(ME->getBeginLoc(), ME->getEndLoc())); 8255ffd83dbSDimitry Andric return true; 8265ffd83dbSDimitry Andric } 8275ffd83dbSDimitry Andric 8285ffd83dbSDimitry Andric bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) { 8295ffd83dbSDimitry Andric SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use) 8305ffd83dbSDimitry Andric << (!SemaRef.getLangOpts().ObjC); 8315ffd83dbSDimitry Andric return true; 8325ffd83dbSDimitry Andric } 8335ffd83dbSDimitry Andric 8345ffd83dbSDimitry Andric bool VisitTypeLoc(TypeLoc Ty); 8355ffd83dbSDimitry Andric }; 8365ffd83dbSDimitry Andric 8375ffd83dbSDimitry Andric void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( 8385ffd83dbSDimitry Andric NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) { 8395ffd83dbSDimitry Andric AvailabilityResult Result; 8405ffd83dbSDimitry Andric const NamedDecl *OffendingDecl; 8415ffd83dbSDimitry Andric std::tie(Result, OffendingDecl) = 8425ffd83dbSDimitry Andric ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass); 8435ffd83dbSDimitry Andric if (Result != AR_Available) { 8445ffd83dbSDimitry Andric // All other diagnostic kinds have already been handled in 8455ffd83dbSDimitry Andric // DiagnoseAvailabilityOfDecl. 8465ffd83dbSDimitry Andric if (Result != AR_NotYetIntroduced) 8475ffd83dbSDimitry Andric return; 8485ffd83dbSDimitry Andric 8495ffd83dbSDimitry Andric const AvailabilityAttr *AA = 8505ffd83dbSDimitry Andric getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl); 851*0fca6ea1SDimitry Andric assert(AA != nullptr && "expecting valid availability attribute"); 852*0fca6ea1SDimitry Andric bool EnvironmentMatchesOrNone = 853*0fca6ea1SDimitry Andric hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA); 8545ffd83dbSDimitry Andric VersionTuple Introduced = AA->getIntroduced(); 8555ffd83dbSDimitry Andric 856*0fca6ea1SDimitry Andric if (EnvironmentMatchesOrNone && AvailabilityStack.back() >= Introduced) 8575ffd83dbSDimitry Andric return; 8585ffd83dbSDimitry Andric 8595ffd83dbSDimitry Andric // If the context of this function is less available than D, we should not 8605ffd83dbSDimitry Andric // emit a diagnostic. 861*0fca6ea1SDimitry Andric if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, 862*0fca6ea1SDimitry Andric AA->getEnvironment(), Ctx, 8635ffd83dbSDimitry Andric OffendingDecl)) 8645ffd83dbSDimitry Andric return; 8655ffd83dbSDimitry Andric 866*0fca6ea1SDimitry Andric const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo(); 867*0fca6ea1SDimitry Andric std::string PlatformName( 868*0fca6ea1SDimitry Andric AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); 869*0fca6ea1SDimitry Andric llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName()); 870*0fca6ea1SDimitry Andric llvm::StringRef AttrEnvironment = 871*0fca6ea1SDimitry Andric AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; 872*0fca6ea1SDimitry Andric bool UseEnvironment = 873*0fca6ea1SDimitry Andric (!AttrEnvironment.empty() && !TargetEnvironment.empty()); 8745ffd83dbSDimitry Andric 875*0fca6ea1SDimitry Andric unsigned DiagKind = getAvailabilityDiagnosticKind( 876*0fca6ea1SDimitry Andric SemaRef.Context, 877*0fca6ea1SDimitry Andric SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced, 878*0fca6ea1SDimitry Andric EnvironmentMatchesOrNone); 8795ffd83dbSDimitry Andric 8805ffd83dbSDimitry Andric SemaRef.Diag(Range.getBegin(), DiagKind) 881*0fca6ea1SDimitry Andric << Range << D << PlatformName << Introduced.getAsString() 882*0fca6ea1SDimitry Andric << UseEnvironment << TargetEnvironment; 8835ffd83dbSDimitry Andric 8845ffd83dbSDimitry Andric SemaRef.Diag(OffendingDecl->getLocation(), 8855ffd83dbSDimitry Andric diag::note_partial_availability_specified_here) 8865ffd83dbSDimitry Andric << OffendingDecl << PlatformName << Introduced.getAsString() 887*0fca6ea1SDimitry Andric << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString() 888*0fca6ea1SDimitry Andric << UseEnvironment << AttrEnvironment << TargetEnvironment; 889*0fca6ea1SDimitry Andric 890*0fca6ea1SDimitry Andric // Do not offer to silence the warning or fixits for HLSL 891*0fca6ea1SDimitry Andric if (SemaRef.getLangOpts().HLSL) 892*0fca6ea1SDimitry Andric return; 8935ffd83dbSDimitry Andric 8945ffd83dbSDimitry Andric auto FixitDiag = 8955ffd83dbSDimitry Andric SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) 8965ffd83dbSDimitry Andric << Range << D 8975ffd83dbSDimitry Andric << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0 8985ffd83dbSDimitry Andric : /*__builtin_available*/ 1); 8995ffd83dbSDimitry Andric 9005ffd83dbSDimitry Andric // Find the statement which should be enclosed in the if @available check. 9015ffd83dbSDimitry Andric if (StmtStack.empty()) 9025ffd83dbSDimitry Andric return; 9035ffd83dbSDimitry Andric const Stmt *StmtOfUse = StmtStack.back(); 9045ffd83dbSDimitry Andric const CompoundStmt *Scope = nullptr; 9055ffd83dbSDimitry Andric for (const Stmt *S : llvm::reverse(StmtStack)) { 9065ffd83dbSDimitry Andric if (const auto *CS = dyn_cast<CompoundStmt>(S)) { 9075ffd83dbSDimitry Andric Scope = CS; 9085ffd83dbSDimitry Andric break; 9095ffd83dbSDimitry Andric } 9105ffd83dbSDimitry Andric if (isBodyLikeChildStmt(StmtOfUse, S)) { 9115ffd83dbSDimitry Andric // The declaration won't be seen outside of the statement, so we don't 9125ffd83dbSDimitry Andric // have to wrap the uses of any declared variables in if (@available). 9135ffd83dbSDimitry Andric // Therefore we can avoid setting Scope here. 9145ffd83dbSDimitry Andric break; 9155ffd83dbSDimitry Andric } 9165ffd83dbSDimitry Andric StmtOfUse = S; 9175ffd83dbSDimitry Andric } 9185ffd83dbSDimitry Andric const Stmt *LastStmtOfUse = nullptr; 9195ffd83dbSDimitry Andric if (isa<DeclStmt>(StmtOfUse) && Scope) { 9205ffd83dbSDimitry Andric for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) { 9215ffd83dbSDimitry Andric if (StmtUSEFinder::isContained(StmtStack.back(), D)) { 9225ffd83dbSDimitry Andric LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope); 9235ffd83dbSDimitry Andric break; 9245ffd83dbSDimitry Andric } 9255ffd83dbSDimitry Andric } 9265ffd83dbSDimitry Andric } 9275ffd83dbSDimitry Andric 9285ffd83dbSDimitry Andric const SourceManager &SM = SemaRef.getSourceManager(); 9295ffd83dbSDimitry Andric SourceLocation IfInsertionLoc = 9305ffd83dbSDimitry Andric SM.getExpansionLoc(StmtOfUse->getBeginLoc()); 9315ffd83dbSDimitry Andric SourceLocation StmtEndLoc = 9325ffd83dbSDimitry Andric SM.getExpansionRange( 9335ffd83dbSDimitry Andric (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc()) 9345ffd83dbSDimitry Andric .getEnd(); 9355ffd83dbSDimitry Andric if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc)) 9365ffd83dbSDimitry Andric return; 9375ffd83dbSDimitry Andric 9385ffd83dbSDimitry Andric StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM); 9395ffd83dbSDimitry Andric const char *ExtraIndentation = " "; 9405ffd83dbSDimitry Andric std::string FixItString; 9415ffd83dbSDimitry Andric llvm::raw_string_ostream FixItOS(FixItString); 9425ffd83dbSDimitry Andric FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available" 9435ffd83dbSDimitry Andric : "__builtin_available") 9445ffd83dbSDimitry Andric << "(" 9455ffd83dbSDimitry Andric << AvailabilityAttr::getPlatformNameSourceSpelling( 9465ffd83dbSDimitry Andric SemaRef.getASTContext().getTargetInfo().getPlatformName()) 9475ffd83dbSDimitry Andric << " " << Introduced.getAsString() << ", *)) {\n" 9485ffd83dbSDimitry Andric << Indentation << ExtraIndentation; 9495ffd83dbSDimitry Andric FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str()); 9505ffd83dbSDimitry Andric SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken( 9515ffd83dbSDimitry Andric StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(), 9525ffd83dbSDimitry Andric /*SkipTrailingWhitespaceAndNewLine=*/false); 9535ffd83dbSDimitry Andric if (ElseInsertionLoc.isInvalid()) 9545ffd83dbSDimitry Andric ElseInsertionLoc = 9555ffd83dbSDimitry Andric Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts()); 9565ffd83dbSDimitry Andric FixItOS.str().clear(); 9575ffd83dbSDimitry Andric FixItOS << "\n" 9585ffd83dbSDimitry Andric << Indentation << "} else {\n" 9595ffd83dbSDimitry Andric << Indentation << ExtraIndentation 9605ffd83dbSDimitry Andric << "// Fallback on earlier versions\n" 9615ffd83dbSDimitry Andric << Indentation << "}"; 9625ffd83dbSDimitry Andric FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str()); 9635ffd83dbSDimitry Andric } 9645ffd83dbSDimitry Andric } 9655ffd83dbSDimitry Andric 9665ffd83dbSDimitry Andric bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { 9675ffd83dbSDimitry Andric const Type *TyPtr = Ty.getTypePtr(); 9685ffd83dbSDimitry Andric SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()}; 9695ffd83dbSDimitry Andric 9705ffd83dbSDimitry Andric if (Range.isInvalid()) 9715ffd83dbSDimitry Andric return true; 9725ffd83dbSDimitry Andric 9735ffd83dbSDimitry Andric if (const auto *TT = dyn_cast<TagType>(TyPtr)) { 9745ffd83dbSDimitry Andric TagDecl *TD = TT->getDecl(); 9755ffd83dbSDimitry Andric DiagnoseDeclAvailability(TD, Range); 9765ffd83dbSDimitry Andric 9775ffd83dbSDimitry Andric } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) { 9785ffd83dbSDimitry Andric TypedefNameDecl *D = TD->getDecl(); 9795ffd83dbSDimitry Andric DiagnoseDeclAvailability(D, Range); 9805ffd83dbSDimitry Andric 9815ffd83dbSDimitry Andric } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) { 9825ffd83dbSDimitry Andric if (NamedDecl *D = ObjCO->getInterface()) 9835ffd83dbSDimitry Andric DiagnoseDeclAvailability(D, Range); 9845ffd83dbSDimitry Andric } 9855ffd83dbSDimitry Andric 9865ffd83dbSDimitry Andric return true; 9875ffd83dbSDimitry Andric } 9885ffd83dbSDimitry Andric 9895ffd83dbSDimitry Andric bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { 9905ffd83dbSDimitry Andric VersionTuple CondVersion; 9915ffd83dbSDimitry Andric if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) { 9925ffd83dbSDimitry Andric CondVersion = E->getVersion(); 9935ffd83dbSDimitry Andric 9945ffd83dbSDimitry Andric // If we're using the '*' case here or if this check is redundant, then we 9955ffd83dbSDimitry Andric // use the enclosing version to check both branches. 9965ffd83dbSDimitry Andric if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) 9975ffd83dbSDimitry Andric return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); 9985ffd83dbSDimitry Andric } else { 9995ffd83dbSDimitry Andric // This isn't an availability checking 'if', we can just continue. 10005ffd83dbSDimitry Andric return Base::TraverseIfStmt(If); 10015ffd83dbSDimitry Andric } 10025ffd83dbSDimitry Andric 10035ffd83dbSDimitry Andric AvailabilityStack.push_back(CondVersion); 10045ffd83dbSDimitry Andric bool ShouldContinue = TraverseStmt(If->getThen()); 10055ffd83dbSDimitry Andric AvailabilityStack.pop_back(); 10065ffd83dbSDimitry Andric 10075ffd83dbSDimitry Andric return ShouldContinue && TraverseStmt(If->getElse()); 10085ffd83dbSDimitry Andric } 10095ffd83dbSDimitry Andric 10105ffd83dbSDimitry Andric } // end anonymous namespace 10115ffd83dbSDimitry Andric 10125ffd83dbSDimitry Andric void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { 10135ffd83dbSDimitry Andric Stmt *Body = nullptr; 10145ffd83dbSDimitry Andric 10155ffd83dbSDimitry Andric if (auto *FD = D->getAsFunction()) { 10165ffd83dbSDimitry Andric Body = FD->getBody(); 1017bdd1243dSDimitry Andric 1018bdd1243dSDimitry Andric if (auto *CD = dyn_cast<CXXConstructorDecl>(FD)) 1019bdd1243dSDimitry Andric for (const CXXCtorInitializer *CI : CD->inits()) 1020bdd1243dSDimitry Andric DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(CI->getInit()); 1021bdd1243dSDimitry Andric 10225ffd83dbSDimitry Andric } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) 10235ffd83dbSDimitry Andric Body = MD->getBody(); 10245ffd83dbSDimitry Andric else if (auto *BD = dyn_cast<BlockDecl>(D)) 10255ffd83dbSDimitry Andric Body = BD->getBody(); 10265ffd83dbSDimitry Andric 10275ffd83dbSDimitry Andric assert(Body && "Need a body here!"); 10285ffd83dbSDimitry Andric 10295ffd83dbSDimitry Andric DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body); 10305ffd83dbSDimitry Andric } 10315ffd83dbSDimitry Andric 1032fe6060f1SDimitry Andric FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() { 1033fe6060f1SDimitry Andric if (FunctionScopes.empty()) 1034fe6060f1SDimitry Andric return nullptr; 1035fe6060f1SDimitry Andric 1036fe6060f1SDimitry Andric // Conservatively search the entire current function scope context for 1037fe6060f1SDimitry Andric // availability violations. This ensures we always correctly analyze nested 1038fe6060f1SDimitry Andric // classes, blocks, lambdas, etc. that may or may not be inside if(@available) 1039fe6060f1SDimitry Andric // checks themselves. 1040fe6060f1SDimitry Andric return FunctionScopes.front(); 1041fe6060f1SDimitry Andric } 1042fe6060f1SDimitry Andric 10435ffd83dbSDimitry Andric void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, 10445ffd83dbSDimitry Andric ArrayRef<SourceLocation> Locs, 10455ffd83dbSDimitry Andric const ObjCInterfaceDecl *UnknownObjCClass, 10465ffd83dbSDimitry Andric bool ObjCPropertyAccess, 10475ffd83dbSDimitry Andric bool AvoidPartialAvailabilityChecks, 10485ffd83dbSDimitry Andric ObjCInterfaceDecl *ClassReceiver) { 10495ffd83dbSDimitry Andric std::string Message; 10505ffd83dbSDimitry Andric AvailabilityResult Result; 10515ffd83dbSDimitry Andric const NamedDecl* OffendingDecl; 10525ffd83dbSDimitry Andric // See if this declaration is unavailable, deprecated, or partial. 10535ffd83dbSDimitry Andric std::tie(Result, OffendingDecl) = 10545ffd83dbSDimitry Andric ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver); 10555ffd83dbSDimitry Andric if (Result == AR_Available) 10565ffd83dbSDimitry Andric return; 10575ffd83dbSDimitry Andric 10585ffd83dbSDimitry Andric if (Result == AR_NotYetIntroduced) { 10595ffd83dbSDimitry Andric if (AvoidPartialAvailabilityChecks) 10605ffd83dbSDimitry Andric return; 10615ffd83dbSDimitry Andric 10625ffd83dbSDimitry Andric // We need to know the @available context in the current function to 10635ffd83dbSDimitry Andric // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that 10645ffd83dbSDimitry Andric // when we're done parsing the current function. 1065fe6060f1SDimitry Andric if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) { 1066fe6060f1SDimitry Andric Context->HasPotentialAvailabilityViolations = true; 10675ffd83dbSDimitry Andric return; 10685ffd83dbSDimitry Andric } 10695ffd83dbSDimitry Andric } 10705ffd83dbSDimitry Andric 10715ffd83dbSDimitry Andric const ObjCPropertyDecl *ObjCPDecl = nullptr; 10725ffd83dbSDimitry Andric if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { 10735ffd83dbSDimitry Andric if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { 10745ffd83dbSDimitry Andric AvailabilityResult PDeclResult = PD->getAvailability(nullptr); 10755ffd83dbSDimitry Andric if (PDeclResult == Result) 10765ffd83dbSDimitry Andric ObjCPDecl = PD; 10775ffd83dbSDimitry Andric } 10785ffd83dbSDimitry Andric } 10795ffd83dbSDimitry Andric 10805ffd83dbSDimitry Andric EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs, 10815ffd83dbSDimitry Andric UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); 10825ffd83dbSDimitry Andric } 1083