diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Sema/SemaAvailability.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Sema/SemaAvailability.cpp | 202 |
1 files changed, 141 insertions, 61 deletions
diff --git a/contrib/llvm-project/clang/lib/Sema/SemaAvailability.cpp b/contrib/llvm-project/clang/lib/Sema/SemaAvailability.cpp index 846a31a79673..17566c226ec8 100644 --- a/contrib/llvm-project/clang/lib/Sema/SemaAvailability.cpp +++ b/contrib/llvm-project/clang/lib/Sema/SemaAvailability.cpp @@ -12,21 +12,45 @@ #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/Sema.h" +#include "clang/Sema/SemaObjC.h" +#include "llvm/ADT/StringRef.h" #include <optional> using namespace clang; using namespace sema; +static bool hasMatchingEnvironmentOrNone(const ASTContext &Context, + const AvailabilityAttr *AA) { + IdentifierInfo *IIEnvironment = AA->getEnvironment(); + auto Environment = Context.getTargetInfo().getTriple().getEnvironment(); + if (!IIEnvironment || Environment == llvm::Triple::UnknownEnvironment) + return true; + + llvm::Triple::EnvironmentType ET = + AvailabilityAttr::getEnvironmentType(IIEnvironment->getName()); + return Environment == ET; +} + static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, const Decl *D) { + AvailabilityAttr const *PartialMatch = nullptr; // Check each AvailabilityAttr to find the one for this platform. + // For multiple attributes with the same platform try to find one for this + // environment. + // The attribute is always on the FunctionDecl, not on the + // FunctionTemplateDecl. + if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D)) + D = FTD->getTemplatedDecl(); for (const auto *A : D->attrs()) { if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) { // FIXME: this is copied from CheckAvailability. We should try to @@ -45,11 +69,15 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, StringRef TargetPlatform = Context.getTargetInfo().getPlatformName(); // Match the platform name. - if (RealizedPlatform == TargetPlatform) - return Avail; + if (RealizedPlatform == TargetPlatform) { + // Find the best matching attribute for this environment + if (hasMatchingEnvironmentOrNone(Context, Avail)) + return Avail; + PartialMatch = Avail; + } } } - return nullptr; + return PartialMatch; } /// The diagnostic we should emit for \c D, and the declaration that @@ -79,6 +107,12 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D, break; } + // For alias templates, get the underlying declaration. + if (const auto *ADecl = dyn_cast<TypeAliasTemplateDecl>(D)) { + D = ADecl->getTemplatedDecl(); + Result = D->getAvailability(Message); + } + // Forward class declarations get their attributes from their definition. if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) { if (IDecl->getDefinition()) { @@ -98,11 +132,11 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D, // For +new, infer availability from -init. if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { - if (S.NSAPIObj && ClassReceiver) { + if (S.ObjC().NSAPIObj && ClassReceiver) { ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod( - S.NSAPIObj->getInitSelector()); + S.ObjC().NSAPIObj->getInitSelector()); if (Init && Result == AR_Available && MD->isClassMethod() && - MD->getSelector() == S.NSAPIObj->getNewSelector() && + MD->getSelector() == S.ObjC().NSAPIObj->getNewSelector() && MD->definedInNSObject(S.getASTContext())) { Result = Init->getAvailability(Message); D = Init; @@ -117,10 +151,9 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D, /// whether we should emit a diagnostic for \c K and \c DeclVersion in /// the context of \c Ctx. For example, we should emit an unavailable diagnostic /// in a deprecated context, but not the other way around. -static bool -ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K, - VersionTuple DeclVersion, Decl *Ctx, - const NamedDecl *OffendingDecl) { +static bool ShouldDiagnoseAvailabilityInContext( + Sema &S, AvailabilityResult K, VersionTuple DeclVersion, + const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) { assert(K != AR_Available && "Expected an unavailable declaration here!"); // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic. @@ -135,11 +168,26 @@ ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K, } } + // In HLSL, skip emitting diagnostic if the diagnostic mode is not set to + // strict (-fhlsl-strict-availability), or if the target is library and the + // availability is restricted to a specific environment/shader stage. + // For libraries the availability will be checked later in + // DiagnoseHLSLAvailability class once where the specific environment/shader + // stage of the caller is known. + if (S.getLangOpts().HLSL) { + if (!S.getLangOpts().HLSLStrictAvailability || + (DeclEnv != nullptr && + S.getASTContext().getTargetInfo().getTriple().getEnvironment() == + llvm::Triple::EnvironmentType::Library)) + return false; + } + // Checks if we should emit the availability diagnostic in the context of C. auto CheckContext = [&](const Decl *C) { if (K == AR_NotYetIntroduced) { if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C)) - if (AA->getIntroduced() >= DeclVersion) + if (AA->getIntroduced() >= DeclVersion && + AA->getEnvironment() == DeclEnv) return true; } else if (K == AR_Deprecated) { if (C->isDeprecated()) @@ -187,13 +235,16 @@ ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K, return true; } -static bool -shouldDiagnoseAvailabilityByDefault(const ASTContext &Context, - const VersionTuple &DeploymentVersion, - const VersionTuple &DeclVersion) { +static unsigned getAvailabilityDiagnosticKind( + const ASTContext &Context, const VersionTuple &DeploymentVersion, + const VersionTuple &DeclVersion, bool HasMatchingEnv) { const auto &Triple = Context.getTargetInfo().getTriple(); VersionTuple ForceAvailabilityFromVersion; switch (Triple.getOS()) { + // For iOS, emit the diagnostic even if -Wunguarded-availability is + // not specified for deployment targets >= to iOS 11 or equivalent or + // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or + // later. case llvm::Triple::IOS: case llvm::Triple::TvOS: ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11); @@ -205,15 +256,26 @@ shouldDiagnoseAvailabilityByDefault(const ASTContext &Context, case llvm::Triple::MacOSX: ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13); break; + // For HLSL, use diagnostic from HLSLAvailability group which + // are reported as errors by default and in strict diagnostic mode + // (-fhlsl-strict-availability) and as warnings in relaxed diagnostic + // mode (-Wno-error=hlsl-availability) case llvm::Triple::ShaderModel: - // Always enable availability diagnostics for shader models. - return true; + return HasMatchingEnv ? diag::warn_hlsl_availability + : diag::warn_hlsl_availability_unavailable; default: - // New targets should always warn about availability. - return Triple.getVendor() == llvm::Triple::Apple; + // New Apple targets should always warn about availability. + ForceAvailabilityFromVersion = + (Triple.getVendor() == llvm::Triple::Apple) + ? VersionTuple(/*Major=*/0, 0) + : VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1); } - return DeploymentVersion >= ForceAvailabilityFromVersion || - DeclVersion >= ForceAvailabilityFromVersion; + if (DeploymentVersion >= ForceAvailabilityFromVersion || + DeclVersion >= ForceAvailabilityFromVersion) + return HasMatchingEnv ? diag::warn_unguarded_availability_new + : diag::warn_unguarded_availability_unavailable_new; + return HasMatchingEnv ? diag::warn_unguarded_availability + : diag::warn_unguarded_availability_unavailable; } static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) { @@ -343,10 +405,14 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, unsigned available_here_select_kind; VersionTuple DeclVersion; - if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl)) + const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl); + const IdentifierInfo *IIEnv = nullptr; + if (AA) { DeclVersion = AA->getIntroduced(); + IIEnv = AA->getEnvironment(); + } - if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx, + if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, IIEnv, Ctx, OffendingDecl)) return; @@ -354,8 +420,7 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, // The declaration can have multiple availability attributes, we are looking // at one of them. - const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl); - if (A && A->isInherited()) { + if (AA && AA->isInherited()) { for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl; Redecl = Redecl->getPreviousDecl()) { const AvailabilityAttr *AForRedecl = @@ -375,26 +440,38 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, // not specified for deployment targets >= to iOS 11 or equivalent or // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or // later. - const AvailabilityAttr *AA = - getAttrForPlatform(S.getASTContext(), OffendingDecl); + assert(AA != nullptr && "expecting valid availability attribute"); VersionTuple Introduced = AA->getIntroduced(); - - bool UseNewWarning = shouldDiagnoseAvailabilityByDefault( + bool EnvironmentMatchesOrNone = + hasMatchingEnvironmentOrNone(S.getASTContext(), AA); + + const TargetInfo &TI = S.getASTContext().getTargetInfo(); + std::string PlatformName( + AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); + llvm::StringRef TargetEnvironment( + llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment())); + llvm::StringRef AttrEnvironment = + AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; + bool UseEnvironment = + (!AttrEnvironment.empty() && !TargetEnvironment.empty()); + + unsigned DiagKind = getAvailabilityDiagnosticKind( S.Context, S.Context.getTargetInfo().getPlatformMinVersion(), - Introduced); - unsigned Warning = UseNewWarning ? diag::warn_unguarded_availability_new - : diag::warn_unguarded_availability; - - std::string PlatformName(AvailabilityAttr::getPrettyPlatformName( - S.getASTContext().getTargetInfo().getPlatformName())); + Introduced, EnvironmentMatchesOrNone); - S.Diag(Loc, Warning) << OffendingDecl << PlatformName - << Introduced.getAsString(); + S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName + << Introduced.getAsString() << UseEnvironment + << TargetEnvironment; S.Diag(OffendingDecl->getLocation(), diag::note_partial_availability_specified_here) << OffendingDecl << PlatformName << Introduced.getAsString() - << S.Context.getTargetInfo().getPlatformMinVersion().getAsString(); + << S.Context.getTargetInfo().getPlatformMinVersion().getAsString() + << UseEnvironment << AttrEnvironment << TargetEnvironment; + + // Do not offer to silence the warning or fixits for HLSL + if (S.getLangOpts().HLSL) + return; if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) { if (const auto *TD = dyn_cast<TagDecl>(Enclosing)) @@ -771,40 +848,48 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( const AvailabilityAttr *AA = getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl); + assert(AA != nullptr && "expecting valid availability attribute"); + bool EnvironmentMatchesOrNone = + hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA); VersionTuple Introduced = AA->getIntroduced(); - if (AvailabilityStack.back() >= Introduced) + if (EnvironmentMatchesOrNone && AvailabilityStack.back() >= Introduced) return; // If the context of this function is less available than D, we should not // emit a diagnostic. - if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx, + if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, + AA->getEnvironment(), Ctx, OffendingDecl)) return; - // We would like to emit the diagnostic even if -Wunguarded-availability is - // not specified for deployment targets >= to iOS 11 or equivalent or - // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or - // later. - unsigned DiagKind = - shouldDiagnoseAvailabilityByDefault( - SemaRef.Context, - SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced) - ? diag::warn_unguarded_availability_new - : diag::warn_unguarded_availability; + const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo(); + std::string PlatformName( + AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); + llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName()); + llvm::StringRef AttrEnvironment = + AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; + bool UseEnvironment = + (!AttrEnvironment.empty() && !TargetEnvironment.empty()); - std::string PlatformName(AvailabilityAttr::getPrettyPlatformName( - SemaRef.getASTContext().getTargetInfo().getPlatformName())); + unsigned DiagKind = getAvailabilityDiagnosticKind( + SemaRef.Context, + SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced, + EnvironmentMatchesOrNone); SemaRef.Diag(Range.getBegin(), DiagKind) - << Range << D << PlatformName << Introduced.getAsString(); + << Range << D << PlatformName << Introduced.getAsString() + << UseEnvironment << TargetEnvironment; SemaRef.Diag(OffendingDecl->getLocation(), diag::note_partial_availability_specified_here) << OffendingDecl << PlatformName << Introduced.getAsString() - << SemaRef.Context.getTargetInfo() - .getPlatformMinVersion() - .getAsString(); + << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString() + << UseEnvironment << AttrEnvironment << TargetEnvironment; + + // Do not offer to silence the warning or fixits for HLSL + if (SemaRef.getLangOpts().HLSL) + return; auto FixitDiag = SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) @@ -928,11 +1013,6 @@ void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { Stmt *Body = nullptr; if (auto *FD = D->getAsFunction()) { - // FIXME: We only examine the pattern decl for availability violations now, - // but we should also examine instantiated templates. - if (FD->isTemplateInstantiation()) - return; - Body = FD->getBody(); if (auto *CD = dyn_cast<CXXConstructorDecl>(FD)) |