diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Sema/SemaDeclAttr.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Sema/SemaDeclAttr.cpp | 874 |
1 files changed, 768 insertions, 106 deletions
diff --git a/contrib/llvm/tools/clang/lib/Sema/SemaDeclAttr.cpp b/contrib/llvm/tools/clang/lib/Sema/SemaDeclAttr.cpp index c6a5bc7..2a310bf 100644 --- a/contrib/llvm/tools/clang/lib/Sema/SemaDeclAttr.cpp +++ b/contrib/llvm/tools/clang/lib/Sema/SemaDeclAttr.cpp @@ -218,21 +218,45 @@ static bool checkAttributeAtMostNumArgs(Sema &S, const AttributeList &Attr, std::greater<unsigned>()); } +/// \brief A helper function to provide Attribute Location for the Attr types +/// AND the AttributeList. +template <typename AttrInfo> +static typename std::enable_if<std::is_base_of<clang::Attr, AttrInfo>::value, + SourceLocation>::type +getAttrLoc(const AttrInfo &Attr) { + return Attr.getLocation(); +} +static SourceLocation getAttrLoc(const clang::AttributeList &Attr) { + return Attr.getLoc(); +} + +/// \brief A helper function to provide Attribute Name for the Attr types +/// AND the AttributeList. +template <typename AttrInfo> +static typename std::enable_if<std::is_base_of<clang::Attr, AttrInfo>::value, + const AttrInfo *>::type +getAttrName(const AttrInfo &Attr) { + return &Attr; +} +static const IdentifierInfo *getAttrName(const clang::AttributeList &Attr) { + return Attr.getName(); +} + /// \brief If Expr is a valid integer constant, get the value of the integer /// expression and return success or failure. May output an error. -static bool checkUInt32Argument(Sema &S, const AttributeList &Attr, - const Expr *Expr, uint32_t &Val, - unsigned Idx = UINT_MAX) { +template<typename AttrInfo> +static bool checkUInt32Argument(Sema &S, const AttrInfo& Attr, const Expr *Expr, + uint32_t &Val, unsigned Idx = UINT_MAX) { llvm::APSInt I(32); if (Expr->isTypeDependent() || Expr->isValueDependent() || !Expr->isIntegerConstantExpr(I, S.Context)) { if (Idx != UINT_MAX) - S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type) - << Attr.getName() << Idx << AANT_ArgumentIntegerConstant + S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_n_type) + << getAttrName(Attr) << Idx << AANT_ArgumentIntegerConstant << Expr->getSourceRange(); else - S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) - << Attr.getName() << AANT_ArgumentIntegerConstant + S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_type) + << getAttrName(Attr) << AANT_ArgumentIntegerConstant << Expr->getSourceRange(); return false; } @@ -250,9 +274,9 @@ static bool checkUInt32Argument(Sema &S, const AttributeList &Attr, /// \brief Wrapper around checkUInt32Argument, with an extra check to be sure /// that the result will fit into a regular (signed) int. All args have the same /// purpose as they do in checkUInt32Argument. -static bool checkPositiveIntArgument(Sema &S, const AttributeList &Attr, - const Expr *Expr, int &Val, - unsigned Idx = UINT_MAX) { +template<typename AttrInfo> +static bool checkPositiveIntArgument(Sema &S, const AttrInfo& Attr, const Expr *Expr, + int &Val, unsigned Idx = UINT_MAX) { uint32_t UVal; if (!checkUInt32Argument(S, Attr, Expr, UVal, Idx)) return false; @@ -287,11 +311,10 @@ static bool checkAttrMutualExclusion(Sema &S, Decl *D, SourceRange Range, /// instance method D. May output an error. /// /// \returns true if IdxExpr is a valid index. -static bool checkFunctionOrMethodParameterIndex(Sema &S, const Decl *D, - const AttributeList &Attr, - unsigned AttrArgNum, - const Expr *IdxExpr, - uint64_t &Idx) { +template <typename AttrInfo> +static bool checkFunctionOrMethodParameterIndex( + Sema &S, const Decl *D, const AttrInfo &Attr, unsigned AttrArgNum, + const Expr *IdxExpr, uint64_t &Idx, bool AllowImplicitThis = false) { assert(isFunctionOrMethodOrBlock(D)); // In C++ the implicit 'this' function parameter also counts. @@ -305,24 +328,24 @@ static bool checkFunctionOrMethodParameterIndex(Sema &S, const Decl *D, llvm::APSInt IdxInt; if (IdxExpr->isTypeDependent() || IdxExpr->isValueDependent() || !IdxExpr->isIntegerConstantExpr(IdxInt, S.Context)) { - S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type) - << Attr.getName() << AttrArgNum << AANT_ArgumentIntegerConstant + S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_n_type) + << getAttrName(Attr) << AttrArgNum << AANT_ArgumentIntegerConstant << IdxExpr->getSourceRange(); return false; } Idx = IdxInt.getLimitedValue(); if (Idx < 1 || (!IV && Idx > NumParams)) { - S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds) - << Attr.getName() << AttrArgNum << IdxExpr->getSourceRange(); + S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_out_of_bounds) + << getAttrName(Attr) << AttrArgNum << IdxExpr->getSourceRange(); return false; } Idx--; // Convert to zero-based. - if (HasImplicitThisParam) { + if (HasImplicitThisParam && !AllowImplicitThis) { if (Idx == 0) { - S.Diag(Attr.getLoc(), + S.Diag(getAttrLoc(Attr), diag::err_attribute_invalid_implicit_this_argument) - << Attr.getName() << IdxExpr->getSourceRange(); + << getAttrName(Attr) << IdxExpr->getSourceRange(); return false; } --Idx; @@ -753,31 +776,48 @@ static void handleAssertExclusiveLockAttr(Sema &S, Decl *D, Attr.getAttributeSpellingListIndex())); } -/// \brief Checks to be sure that the given parameter number is inbounds, and is -/// an some integral type. Will emit appropriate diagnostics if this returns +/// \brief Checks to be sure that the given parameter number is in bounds, and is +/// an integral type. Will emit appropriate diagnostics if this returns /// false. /// /// FuncParamNo is expected to be from the user, so is base-1. AttrArgNo is used /// to actually retrieve the argument, so it's base-0. +template <typename AttrInfo> static bool checkParamIsIntegerType(Sema &S, const FunctionDecl *FD, - const AttributeList &Attr, - unsigned FuncParamNo, unsigned AttrArgNo) { - assert(Attr.isArgExpr(AttrArgNo) && "Expected expression argument"); + const AttrInfo &Attr, Expr *AttrArg, + unsigned FuncParamNo, unsigned AttrArgNo, + bool AllowDependentType = false) { uint64_t Idx; - if (!checkFunctionOrMethodParameterIndex(S, FD, Attr, FuncParamNo, - Attr.getArgAsExpr(AttrArgNo), Idx)) + if (!checkFunctionOrMethodParameterIndex(S, FD, Attr, FuncParamNo, AttrArg, + Idx)) return false; const ParmVarDecl *Param = FD->getParamDecl(Idx); + if (AllowDependentType && Param->getType()->isDependentType()) + return true; if (!Param->getType()->isIntegerType() && !Param->getType()->isCharType()) { - SourceLocation SrcLoc = Attr.getArgAsExpr(AttrArgNo)->getLocStart(); + SourceLocation SrcLoc = AttrArg->getLocStart(); S.Diag(SrcLoc, diag::err_attribute_integers_only) - << Attr.getName() << Param->getSourceRange(); + << getAttrName(Attr) << Param->getSourceRange(); return false; } return true; } +/// \brief Checks to be sure that the given parameter number is in bounds, and is +/// an integral type. Will emit appropriate diagnostics if this returns false. +/// +/// FuncParamNo is expected to be from the user, so is base-1. AttrArgNo is used +/// to actually retrieve the argument, so it's base-0. +static bool checkParamIsIntegerType(Sema &S, const FunctionDecl *FD, + const AttributeList &Attr, + unsigned FuncParamNo, unsigned AttrArgNo, + bool AllowDependentType = false) { + assert(Attr.isArgExpr(AttrArgNo) && "Expected expression argument"); + return checkParamIsIntegerType(S, FD, Attr, Attr.getArgAsExpr(AttrArgNo), + FuncParamNo, AttrArgNo, AllowDependentType); +} + static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (!checkAttributeAtLeastNumArgs(S, Attr, 1) || !checkAttributeAtMostNumArgs(S, Attr, 2)) @@ -792,7 +832,7 @@ static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList &Attr) { const Expr *SizeExpr = Attr.getArgAsExpr(0); int SizeArgNo; - // Paramater indices are 1-indexed, hence Index=1 + // Parameter indices are 1-indexed, hence Index=1 if (!checkPositiveIntArgument(S, Attr, SizeExpr, SizeArgNo, /*Index=*/1)) return; @@ -803,7 +843,7 @@ static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList &Attr) { int NumberArgNo = 0; if (Attr.getNumArgs() == 2) { const Expr *NumberExpr = Attr.getArgAsExpr(1); - // Paramater indices are 1-based, hence Index=2 + // Parameter indices are 1-based, hence Index=2 if (!checkPositiveIntArgument(S, Attr, NumberExpr, NumberArgNo, /*Index=*/2)) return; @@ -909,7 +949,7 @@ static bool checkFunctionConditionAttr(Sema &S, Decl *D, Msg = "<no message provided>"; SmallVector<PartialDiagnosticAt, 8> Diags; - if (!Cond->isValueDependent() && + if (isa<FunctionDecl>(D) && !Cond->isValueDependent() && !Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(D), Diags)) { S.Diag(Attr.getLoc(), diag::err_attr_cond_never_constant_expr) @@ -997,10 +1037,11 @@ static void handleDiagnoseIfAttr(Sema &S, Decl *D, const AttributeList &Attr) { return; } - auto *FD = cast<FunctionDecl>(D); - bool ArgDependent = ArgumentDependenceChecker(FD).referencesArgs(Cond); + bool ArgDependent = false; + if (const auto *FD = dyn_cast<FunctionDecl>(D)) + ArgDependent = ArgumentDependenceChecker(FD).referencesArgs(Cond); D->addAttr(::new (S.Context) DiagnoseIfAttr( - Attr.getRange(), S.Context, Cond, Msg, DiagType, ArgDependent, FD, + Attr.getRange(), S.Context, Cond, Msg, DiagType, ArgDependent, cast<NamedDecl>(D), Attr.getAttributeSpellingListIndex())); } @@ -1420,7 +1461,7 @@ static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check if the attribute came from a macro expansion or a template // instantiation. if (NonNullArgs.empty() && Attr.getLoc().isFileID() && - S.ActiveTemplateInstantiations.empty()) { + !S.inTemplateInstantiation()) { bool AnyPointers = isFunctionOrMethodVariadic(D); for (unsigned I = 0, E = getFunctionOrMethodNumParams(D); I != E && !AnyPointers; ++I) { @@ -1484,6 +1525,12 @@ static void handleAssumeAlignedAttr(Sema &S, Decl *D, Attr.getAttributeSpellingListIndex()); } +static void handleAllocAlignAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + S.AddAllocAlignAttr(Attr.getRange(), D, Attr.getArgAsExpr(0), + Attr.getAttributeSpellingListIndex()); +} + void Sema::AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, Expr *OE, unsigned SpellingListIndex) { QualType ResultType = getFunctionOrMethodResultType(D); @@ -1535,6 +1582,44 @@ void Sema::AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, AssumeAlignedAttr(AttrRange, Context, E, OE, SpellingListIndex)); } +void Sema::AddAllocAlignAttr(SourceRange AttrRange, Decl *D, Expr *ParamExpr, + unsigned SpellingListIndex) { + QualType ResultType = getFunctionOrMethodResultType(D); + + AllocAlignAttr TmpAttr(AttrRange, Context, 0, SpellingListIndex); + SourceLocation AttrLoc = AttrRange.getBegin(); + + if (!ResultType->isDependentType() && + !isValidPointerAttrType(ResultType, /* RefOkay */ true)) { + Diag(AttrLoc, diag::warn_attribute_return_pointers_refs_only) + << &TmpAttr << AttrRange << getFunctionOrMethodResultSourceRange(D); + return; + } + + uint64_t IndexVal; + const auto *FuncDecl = cast<FunctionDecl>(D); + if (!checkFunctionOrMethodParameterIndex(*this, FuncDecl, TmpAttr, + /*AttrArgNo=*/1, ParamExpr, + IndexVal)) + return; + + QualType Ty = getFunctionOrMethodParamType(D, IndexVal); + if (!Ty->isDependentType() && !Ty->isIntegralType(Context)) { + Diag(ParamExpr->getLocStart(), diag::err_attribute_integers_only) + << &TmpAttr << FuncDecl->getParamDecl(IndexVal)->getSourceRange(); + return; + } + + // We cannot use the Idx returned from checkFunctionOrMethodParameterIndex + // because that has corrected for the implicit this parameter, and is zero- + // based. The attribute expects what the user wrote explicitly. + llvm::APSInt Val; + ParamExpr->EvaluateAsInt(Val, Context); + + D->addAttr(::new (Context) AllocAlignAttr( + AttrRange, Context, Val.getZExtValue(), SpellingListIndex)); +} + /// Normalize the attribute, __foo__ becomes foo. /// Returns true if normalization was applied. static bool normalizeName(StringRef &AttrName) { @@ -1839,6 +1924,17 @@ static void handleNakedAttr(Sema &S, Decl *D, const AttributeList &Attr) { Attr.getName())) return; + if (Attr.isDeclspecAttribute()) { + const auto &Triple = S.getASTContext().getTargetInfo().getTriple(); + const auto &Arch = Triple.getArch(); + if (Arch != llvm::Triple::x86 && + (Arch != llvm::Triple::arm && Arch != llvm::Triple::thumb)) { + S.Diag(Attr.getLoc(), diag::err_attribute_not_supported_on_arch) + << Attr.getName() << Triple.getArchName(); + return; + } + } + D->addAttr(::new (S.Context) NakedAttr(Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); } @@ -1846,17 +1942,26 @@ static void handleNakedAttr(Sema &S, Decl *D, const AttributeList &Attr) { static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &attr) { if (hasDeclarator(D)) return; - if (S.CheckNoReturnAttr(attr)) return; + if (S.CheckNoReturnAttr(attr)) + return; if (!isa<ObjCMethodDecl>(D)) { S.Diag(attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << attr.getName() << ExpectedFunctionOrMethod; + << attr.getName() << ExpectedFunctionOrMethod; return; } - D->addAttr(::new (S.Context) - NoReturnAttr(attr.getRange(), S.Context, - attr.getAttributeSpellingListIndex())); + D->addAttr(::new (S.Context) NoReturnAttr( + attr.getRange(), S.Context, attr.getAttributeSpellingListIndex())); +} + +static void handleNoCallerSavedRegsAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (S.CheckNoCallerSavedRegsAttr(Attr)) + return; + + D->addAttr(::new (S.Context) AnyX86NoCallerSavedRegistersAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); } bool Sema::CheckNoReturnAttr(const AttributeList &attr) { @@ -1868,6 +1973,22 @@ bool Sema::CheckNoReturnAttr(const AttributeList &attr) { return false; } +bool Sema::CheckNoCallerSavedRegsAttr(const AttributeList &Attr) { + // Check whether the attribute is valid on the current target. + if (!Attr.existsInTarget(Context.getTargetInfo())) { + Diag(Attr.getLoc(), diag::warn_unknown_attribute_ignored) << Attr.getName(); + Attr.setInvalid(); + return true; + } + + if (!checkAttributeNumArgs(*this, Attr, 0)) { + Attr.setInvalid(); + return true; + } + + return false; +} + static void handleAnalyzerNoReturnAttr(Sema &S, Decl *D, const AttributeList &Attr) { @@ -2303,10 +2424,8 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, << Platform->Ident; NamedDecl *ND = dyn_cast<NamedDecl>(D); - if (!ND) { - S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); + if (!ND) // We warned about this already, so just return. return; - } AvailabilityChange Introduced = Attr.getAvailabilityIntroduced(); AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated(); @@ -2409,6 +2528,26 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, } } +static void handleExternalSourceSymbolAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) + return; + assert(checkAttributeAtMostNumArgs(S, Attr, 3) && + "Invalid number of arguments in an external_source_symbol attribute"); + + StringRef Language; + if (const auto *SE = dyn_cast_or_null<StringLiteral>(Attr.getArgAsExpr(0))) + Language = SE->getString(); + StringRef DefinedIn; + if (const auto *SE = dyn_cast_or_null<StringLiteral>(Attr.getArgAsExpr(1))) + DefinedIn = SE->getString(); + bool IsGeneratedDeclaration = Attr.getArgAsIdent(2) != nullptr; + + D->addAttr(::new (S.Context) ExternalSourceSymbolAttr( + Attr.getRange(), S.Context, Language, DefinedIn, IsGeneratedDeclaration, + Attr.getAttributeSpellingListIndex())); +} + template <class T> static T *mergeVisibilityAttr(Sema &S, Decl *D, SourceRange range, typename T::VisibilityType value, @@ -2753,6 +2892,28 @@ static void handleWorkGroupSize(Sema &S, Decl *D, Attr.getAttributeSpellingListIndex())); } +// Handles intel_reqd_sub_group_size. +static void handleSubGroupSize(Sema &S, Decl *D, const AttributeList &Attr) { + uint32_t SGSize; + const Expr *E = Attr.getArgAsExpr(0); + if (!checkUInt32Argument(S, Attr, E, SGSize)) + return; + if (SGSize == 0) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_is_zero) + << Attr.getName() << E->getSourceRange(); + return; + } + + OpenCLIntelReqdSubGroupSizeAttr *Existing = + D->getAttr<OpenCLIntelReqdSubGroupSizeAttr>(); + if (Existing && Existing->getSubGroupSize() != SGSize) + S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) << Attr.getName(); + + D->addAttr(::new (S.Context) OpenCLIntelReqdSubGroupSizeAttr( + Attr.getRange(), S.Context, SGSize, + Attr.getAttributeSpellingListIndex())); +} + static void handleVecTypeHint(Sema &S, Decl *D, const AttributeList &Attr) { if (!Attr.hasParsedType()) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) @@ -2917,6 +3078,28 @@ static void handleCleanupAttr(Sema &S, Decl *D, const AttributeList &Attr) { Attr.getAttributeSpellingListIndex())); } +static void handleEnumExtensibilityAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (!Attr.isArgIdent(0)) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type) + << Attr.getName() << 0 << AANT_ArgumentIdentifier; + return; + } + + EnumExtensibilityAttr::Kind ExtensibilityKind; + IdentifierInfo *II = Attr.getArgAsIdent(0)->Ident; + if (!EnumExtensibilityAttr::ConvertStrToKind(II->getName(), + ExtensibilityKind)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_type_not_supported) + << Attr.getName() << II; + return; + } + + D->addAttr(::new (S.Context) EnumExtensibilityAttr( + Attr.getRange(), S.Context, ExtensibilityKind, + Attr.getAttributeSpellingListIndex())); +} + /// Handle __attribute__((format_arg((idx)))) attribute based on /// http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html static void handleFormatArgAttr(Sema &S, Decl *D, const AttributeList &Attr) { @@ -3193,8 +3376,9 @@ static void handleTransparentUnionAttr(Sema &S, Decl *D, } if (!RD->isCompleteDefinition()) { - S.Diag(Attr.getLoc(), - diag::warn_transparent_union_attribute_not_definition); + if (!RD->isBeingDefined()) + S.Diag(Attr.getLoc(), + diag::warn_transparent_union_attribute_not_definition); return; } @@ -4048,6 +4232,26 @@ static void handleCallConvAttr(Sema &S, Decl *D, const AttributeList &Attr) { } } +static void handleSuppressAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) + return; + + std::vector<StringRef> DiagnosticIdentifiers; + for (unsigned I = 0, E = Attr.getNumArgs(); I != E; ++I) { + StringRef RuleName; + + if (!S.checkStringLiteralArgumentAttr(Attr, I, RuleName, nullptr)) + return; + + // FIXME: Warn if the rule name is unknown. This is tricky because only + // clang-tidy knows about available rules. + DiagnosticIdentifiers.push_back(RuleName); + } + D->addAttr(::new (S.Context) SuppressAttr( + Attr.getRange(), S.Context, DiagnosticIdentifiers.data(), + DiagnosticIdentifiers.size(), Attr.getAttributeSpellingListIndex())); +} + bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, const FunctionDecl *FD) { if (attr.isInvalid()) @@ -4076,7 +4280,7 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, case AttributeList::AT_RegCall: CC = CC_X86RegCall; break; case AttributeList::AT_MSABI: CC = Context.getTargetInfo().getTriple().isOSWindows() ? CC_C : - CC_X86_64Win64; + CC_Win64; break; case AttributeList::AT_SysVABI: CC = Context.getTargetInfo().getTriple().isOSWindows() ? CC_X86_64SysV : @@ -4397,6 +4601,21 @@ static void handleTypeTagForDatatypeAttr(Sema &S, Decl *D, Attr.getAttributeSpellingListIndex())); } +static void handleXRayLogArgsAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + uint64_t ArgCount; + + if (!checkFunctionOrMethodParameterIndex(S, D, Attr, 1, Attr.getArgAsExpr(0), + ArgCount, + true /* AllowImplicitThis*/)) + return; + + // ArgCount isn't a parameter index [0;n), it's a count [1;n] - hence + 1. + D->addAttr(::new (S.Context) + XRayLogArgsAttr(Attr.getRange(), S.Context, ++ArgCount, + Attr.getAttributeSpellingListIndex())); +} + //===----------------------------------------------------------------------===// // Checker-specific attribute handlers. //===----------------------------------------------------------------------===// @@ -4460,6 +4679,16 @@ void Sema::AddNSConsumedAttr(SourceRange attrRange, Decl *D, CFConsumedAttr(attrRange, Context, spellingIndex)); } +bool Sema::checkNSReturnsRetainedReturnType(SourceLocation loc, + QualType type) { + if (isValidSubjectOfNSReturnsRetainedAttribute(type)) + return false; + + Diag(loc, diag::warn_ns_attribute_wrong_return_type) + << "'ns_returns_retained'" << 0 << 0; + return true; +} + static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, const AttributeList &Attr) { QualType returnType; @@ -4481,6 +4710,8 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, << Attr.getRange(); return; } + } else if (Attr.isUsedAsTypeAttr()) { + return; } else { AttributeDeclKind ExpectedDeclKind; switch (Attr.getKind()) { @@ -4524,6 +4755,9 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, } if (!typeOK) { + if (Attr.isUsedAsTypeAttr()) + return; + if (isa<ParmVarDecl>(D)) { S.Diag(D->getLocStart(), diag::warn_ns_attribute_wrong_parameter_type) << Attr.getName() << /*pointer-to-CF*/2 @@ -4863,6 +5097,15 @@ static void handleUuidAttr(Sema &S, Decl *D, const AttributeList &Attr) { } } + // FIXME: It'd be nice to also emit a fixit removing uuid(...) (and, if it's + // the only thing in the [] list, the [] too), and add an insertion of + // __declspec(uuid(...)). But sadly, neither the SourceLocs of the commas + // separating attributes nor of the [ and the ] are in the AST. + // Cf "SourceLocations of attribute list delimiters - [[ ... , ... ]] etc" + // on cfe-dev. + if (Attr.isMicrosoftAttribute()) // Check for [uuid(...)] spelling. + S.Diag(Attr.getLoc(), diag::warn_atl_uuid_deprecated); + UuidAttr *UA = S.mergeUuidAttr(D, Attr.getRange(), Attr.getAttributeSpellingListIndex(), StrRef); if (UA) @@ -5126,6 +5369,32 @@ static void handleAnyX86InterruptAttr(Sema &S, Decl *D, D->addAttr(UsedAttr::CreateImplicit(S.Context)); } +static void handleAVRInterruptAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (!isFunctionOrMethod(D)) { + S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) + << "'interrupt'" << ExpectedFunction; + return; + } + + if (!checkAttributeNumArgs(S, Attr, 0)) + return; + + handleSimpleAttribute<AVRInterruptAttr>(S, D, Attr); +} + +static void handleAVRSignalAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (!isFunctionOrMethod(D)) { + S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) + << "'signal'" << ExpectedFunction; + return; + } + + if (!checkAttributeNumArgs(S, Attr, 0)) + return; + + handleSimpleAttribute<AVRSignalAttr>(S, D, Attr); +} + static void handleInterruptAttr(Sema &S, Decl *D, const AttributeList &Attr) { // Dispatch the interrupt attribute based on the current target. switch (S.Context.getTargetInfo().getTriple().getArch()) { @@ -5140,6 +5409,9 @@ static void handleInterruptAttr(Sema &S, Decl *D, const AttributeList &Attr) { case llvm::Triple::x86_64: handleAnyX86InterruptAttr(S, D, Attr); break; + case llvm::Triple::avr: + handleAVRInterruptAttr(S, D, Attr); + break; default: handleARMInterruptAttr(S, D, Attr); break; @@ -5559,18 +5831,21 @@ static void handleOpenCLNoSVMAttr(Sema &S, Decl *D, const AttributeList &Attr) { static bool handleCommonAttributeFeatures(Sema &S, Scope *scope, Decl *D, const AttributeList &Attr) { // Several attributes carry different semantics than the parsing requires, so - // those are opted out of the common handling. + // those are opted out of the common argument checks. // // We also bail on unknown and ignored attributes because those are handled // as part of the target-specific handling logic. - if (Attr.hasCustomParsing() || - Attr.getKind() == AttributeList::UnknownAttribute) + if (Attr.getKind() == AttributeList::UnknownAttribute) return false; - // Check whether the attribute requires specific language extensions to be // enabled. if (!Attr.diagnoseLangOpts(S)) return true; + // Check whether the attribute appertains to the given subject. + if (!Attr.diagnoseAppertainsTo(S, D)) + return true; + if (Attr.hasCustomParsing()) + return false; if (Attr.getMinArgs() == Attr.getMaxArgs()) { // If there are no optional arguments, then checking for the argument count @@ -5587,10 +5862,6 @@ static bool handleCommonAttributeFeatures(Sema &S, Scope *scope, Decl *D, return true; } - // Check whether the attribute appertains to the given subject. - if (!Attr.diagnoseAppertainsTo(S, D)) - return true; - return false; } @@ -5682,12 +5953,18 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, handleDLLAttr(S, D, Attr); break; case AttributeList::AT_Mips16: - handleSimpleAttributeWithExclusions<Mips16Attr, MipsInterruptAttr>(S, D, - Attr); + handleSimpleAttributeWithExclusions<Mips16Attr, MicroMipsAttr, + MipsInterruptAttr>(S, D, Attr); break; case AttributeList::AT_NoMips16: handleSimpleAttribute<NoMips16Attr>(S, D, Attr); break; + case AttributeList::AT_MicroMips: + handleSimpleAttributeWithExclusions<MicroMipsAttr, Mips16Attr>(S, D, Attr); + break; + case AttributeList::AT_NoMicroMips: + handleSimpleAttribute<NoMicroMipsAttr>(S, D, Attr); + break; case AttributeList::AT_AMDGPUFlatWorkGroupSize: handleAMDGPUFlatWorkGroupSizeAttr(S, D, Attr); break; @@ -5700,6 +5977,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_AMDGPUNumVGPR: handleAMDGPUNumVGPRAttr(S, D, Attr); break; + case AttributeList::AT_AVRSignal: + handleAVRSignalAttr(S, D, Attr); + break; case AttributeList::AT_IBAction: handleSimpleAttribute<IBActionAttr>(S, D, Attr); break; @@ -5772,6 +6052,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ExtVectorType: handleExtVectorTypeAttr(S, scope, D, Attr); break; + case AttributeList::AT_ExternalSourceSymbol: + handleExternalSourceSymbolAttr(S, D, Attr); + break; case AttributeList::AT_MinSize: handleMinSizeAttr(S, D, Attr); break; @@ -5781,6 +6064,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_FlagEnum: handleSimpleAttribute<FlagEnumAttr>(S, D, Attr); break; + case AttributeList::AT_EnumExtensibility: + handleEnumExtensibilityAttr(S, D, Attr); + break; case AttributeList::AT_Flatten: handleSimpleAttribute<FlattenAttr>(S, D, Attr); break; @@ -5837,6 +6123,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_AssumeAligned: handleAssumeAlignedAttr(S, D, Attr); break; + case AttributeList::AT_AllocAlign: + handleAllocAlignAttr(S, D, Attr); + break; case AttributeList::AT_Overloadable: handleSimpleAttribute<OverloadableAttr>(S, D, Attr); break; @@ -5923,6 +6212,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ReqdWorkGroupSize: handleWorkGroupSize<ReqdWorkGroupSizeAttr>(S, D, Attr); break; + case AttributeList::AT_OpenCLIntelReqdSubGroupSize: + handleSubGroupSize(S, D, Attr); + break; case AttributeList::AT_VecTypeHint: handleVecTypeHint(S, D, Attr); break; @@ -6056,6 +6348,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_PreserveAll: handleCallConvAttr(S, D, Attr); break; + case AttributeList::AT_Suppress: + handleSuppressAttr(S, D, Attr); + break; case AttributeList::AT_OpenCLKernel: handleSimpleAttribute<OpenCLKernelAttr>(S, D, Attr); break; @@ -6216,6 +6511,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_TypeTagForDatatype: handleTypeTagForDatatypeAttr(S, D, Attr); break; + case AttributeList::AT_AnyX86NoCallerSavedRegisters: + handleNoCallerSavedRegsAttr(S, D, Attr); + break; case AttributeList::AT_RenderScriptKernel: handleSimpleAttribute<RenderScriptKernelAttr>(S, D, Attr); break; @@ -6223,6 +6521,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_XRayInstrument: handleSimpleAttribute<XRayInstrumentAttr>(S, D, Attr); break; + case AttributeList::AT_XRayLogArgs: + handleXRayLogArgsAttr(S, D, Attr); + break; } } @@ -6278,10 +6579,22 @@ void Sema::ProcessDeclAttributeList(Scope *S, Decl *D, Diag(D->getLocation(), diag::err_attribute_wrong_decl_type) << A << ExpectedKernelFunction; D->setInvalidDecl(); + } else if (Attr *A = D->getAttr<OpenCLIntelReqdSubGroupSizeAttr>()) { + Diag(D->getLocation(), diag::err_opencl_kernel_attr) << A; + D->setInvalidDecl(); } } } +// Helper for delayed processing TransparentUnion attribute. +void Sema::ProcessDeclAttributeDelayed(Decl *D, const AttributeList *AttrList) { + for (const AttributeList *Attr = AttrList; Attr; Attr = Attr->getNext()) + if (Attr->getKind() == AttributeList::AT_TransparentUnion) { + handleTransparentUnionAttr(*this, D, *Attr); + break; + } +} + // Annotation attributes are the only attributes allowed after an access // specifier. bool Sema::ProcessAccessDeclAttributeList(AccessSpecDecl *ASDecl, @@ -6443,6 +6756,9 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) { // Finally, apply any attributes on the decl itself. if (const AttributeList *Attrs = PD.getAttributes()) ProcessDeclAttributeList(S, D, Attrs); + + // Apply additional attributes specified by '#pragma clang attribute'. + AddPragmaAttributes(S, D); } /// Is the given declaration allowed to use a forbidden type? @@ -6537,6 +6853,50 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, return nullptr; } +/// The diagnostic we should emit for \c D, and the declaration that +/// originated it, or \c AR_Available. +/// +/// \param D The declaration to check. +/// \param Message If non-null, this will be populated with the message from +/// the availability attribute that is selected. +static std::pair<AvailabilityResult, const NamedDecl *> +ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message) { + AvailabilityResult Result = D->getAvailability(Message); + + // For typedefs, if the typedef declaration appears available look + // to the underlying type to see if it is more restrictive. + while (const TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) { + if (Result == AR_Available) { + if (const TagType *TT = TD->getUnderlyingType()->getAs<TagType>()) { + D = TT->getDecl(); + Result = D->getAvailability(Message); + continue; + } + } + break; + } + + // Forward class declarations get their attributes from their definition. + if (const ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) { + if (IDecl->getDefinition()) { + D = IDecl->getDefinition(); + Result = D->getAvailability(Message); + } + } + + if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) + if (Result == AR_Available) { + const DeclContext *DC = ECD->getDeclContext(); + if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) { + Result = TheEnumDecl->getAvailability(Message); + D = TheEnumDecl; + } + } + + return {Result, D}; +} + + /// \brief 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. @@ -6602,8 +6962,60 @@ static bool ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K, return true; } +static bool +shouldDiagnoseAvailabilityByDefault(const ASTContext &Context, + const VersionTuple &DeploymentVersion, + const VersionTuple &DeclVersion) { + const auto &Triple = Context.getTargetInfo().getTriple(); + VersionTuple ForceAvailabilityFromVersion; + switch (Triple.getOS()) { + case llvm::Triple::IOS: + case llvm::Triple::TvOS: + ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11); + break; + case llvm::Triple::WatchOS: + ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4); + break; + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13); + break; + default: + // New targets should always warn about availability. + return Triple.getVendor() == llvm::Triple::Apple; + } + return DeploymentVersion >= ForceAvailabilityFromVersion || + DeclVersion >= ForceAvailabilityFromVersion; +} + +static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) { + for (Decl *Ctx = OrigCtx; Ctx; + Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) { + if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx)) + return cast<NamedDecl>(Ctx); + if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) { + if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx)) + return Imp->getClassInterface(); + return CD; + } + } + + return dyn_cast<NamedDecl>(OrigCtx); +} + +/// Actually emit an availability diagnostic for a reference to an unavailable +/// decl. +/// +/// \param Ctx The context that the reference occurred in +/// \param ReferringDecl The exact declaration that was referenced. +/// \param OffendingDecl A related decl to \c ReferringDecl that has an +/// availability attribute corrisponding to \c K attached to it. Note that this +/// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and +/// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl +/// and OffendingDecl is the EnumDecl. static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, - Decl *Ctx, const NamedDecl *D, + Decl *Ctx, const NamedDecl *ReferringDecl, + const NamedDecl *OffendingDecl, StringRef Message, SourceLocation Loc, const ObjCInterfaceDecl *UnknownObjCClass, const ObjCPropertyDecl *ObjCProperty, @@ -6611,6 +7023,7 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, // Diagnostics for deprecated or unavailable. unsigned diag, diag_message, diag_fwdclass_message; unsigned diag_available_here = diag::note_availability_specified_here; + SourceLocation NoteLocation = OffendingDecl->getLocation(); // Matches 'diag::note_property_attribute' options. unsigned property_note_select; @@ -6619,7 +7032,7 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, unsigned available_here_select_kind; VersionTuple DeclVersion; - if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, D)) + if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl)) DeclVersion = AA->getIntroduced(); if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx)) @@ -6633,6 +7046,8 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, diag_fwdclass_message = diag::warn_deprecated_fwdclass_message; property_note_select = /* deprecated */ 0; available_here_select_kind = /* deprecated */ 2; + if (const auto *attr = OffendingDecl->getAttr<DeprecatedAttr>()) + NoteLocation = attr->getLocation(); break; case AR_Unavailable: @@ -6643,13 +7058,14 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, property_note_select = /* unavailable */ 1; available_here_select_kind = /* unavailable */ 0; - if (auto attr = D->getAttr<UnavailableAttr>()) { + if (auto attr = OffendingDecl->getAttr<UnavailableAttr>()) { if (attr->isImplicit() && attr->getImplicitReason()) { // Most of these failures are due to extra restrictions in ARC; // reflect that in the primary diagnostic when applicable. auto flagARCError = [&] { if (S.getLangOpts().ObjCAutoRefCount && - S.getSourceManager().isInSystemHeader(D->getLocation())) + S.getSourceManager().isInSystemHeader( + OffendingDecl->getLocation())) diag = diag::err_unavailable_in_arc; }; @@ -6687,13 +7103,27 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, } break; - case AR_NotYetIntroduced: - diag = diag::warn_partial_availability; - diag_message = diag::warn_partial_message; - diag_fwdclass_message = diag::warn_partial_fwdclass_message; + case AR_NotYetIntroduced: { + // 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. + const AvailabilityAttr *AA = + getAttrForPlatform(S.getASTContext(), OffendingDecl); + VersionTuple Introduced = AA->getIntroduced(); + bool NewWarning = shouldDiagnoseAvailabilityByDefault( + S.Context, S.Context.getTargetInfo().getPlatformMinVersion(), + Introduced); + diag = NewWarning ? diag::warn_partial_availability_new + : diag::warn_partial_availability; + diag_message = NewWarning ? diag::warn_partial_message_new + : diag::warn_partial_message; + diag_fwdclass_message = NewWarning ? diag::warn_partial_fwdclass_message_new + : diag::warn_partial_fwdclass_message; property_note_select = /* partial */ 2; available_here_select_kind = /* partial */ 3; break; + } case AR_Available: llvm_unreachable("Warning for availability of available declaration?"); @@ -6702,9 +7132,9 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, CharSourceRange UseRange; StringRef Replacement; if (K == AR_Deprecated) { - if (auto attr = D->getAttr<DeprecatedAttr>()) + if (auto attr = OffendingDecl->getAttr<DeprecatedAttr>()) Replacement = attr->getReplacement(); - if (auto attr = getAttrForPlatform(S.Context, D)) + if (auto attr = getAttrForPlatform(S.Context, OffendingDecl)) Replacement = attr->getReplacement(); if (!Replacement.empty()) @@ -6713,21 +7143,21 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, } if (!Message.empty()) { - S.Diag(Loc, diag_message) << D << Message + S.Diag(Loc, diag_message) << ReferringDecl << Message << (UseRange.isValid() ? FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint()); if (ObjCProperty) S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) << ObjCProperty->getDeclName() << property_note_select; } else if (!UnknownObjCClass) { - S.Diag(Loc, diag) << D + S.Diag(Loc, diag) << ReferringDecl << (UseRange.isValid() ? FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint()); if (ObjCProperty) S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) << ObjCProperty->getDeclName() << property_note_select; } else { - S.Diag(Loc, diag_fwdclass_message) << D + S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << (UseRange.isValid() ? FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint()); S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); @@ -6735,27 +7165,36 @@ 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, D); + const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl); if (A && A->isInherited()) { - for (const Decl *Redecl = D->getMostRecentDecl(); Redecl; + for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl; Redecl = Redecl->getPreviousDecl()) { const AvailabilityAttr *AForRedecl = getAttrForPlatform(S.Context, Redecl); if (AForRedecl && !AForRedecl->isInherited()) { // If D is a declaration with inherited attributes, the note should // point to the declaration with actual attributes. - S.Diag(Redecl->getLocation(), diag_available_here) << D + S.Diag(Redecl->getLocation(), diag_available_here) << OffendingDecl << available_here_select_kind; break; } } } else - S.Diag(D->getLocation(), diag_available_here) - << D << available_here_select_kind; + S.Diag(NoteLocation, diag_available_here) + << OffendingDecl << available_here_select_kind; if (K == AR_NotYetIntroduced) - S.Diag(Loc, diag::note_partial_availability_silence) << D; + if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) { + if (auto *TD = dyn_cast<TagDecl>(Enclosing)) + if (TD->getDeclName().isEmpty()) { + S.Diag(TD->getLocation(), diag::note_partial_availability_silence) + << /*Anonymous*/1 << TD->getKindName(); + return; + } + S.Diag(Enclosing->getLocation(), diag::note_partial_availability_silence) + << /*Named*/0 << Enclosing; + } } static void handleDelayedAvailabilityCheck(Sema &S, DelayedDiagnostic &DD, @@ -6765,9 +7204,9 @@ static void handleDelayedAvailabilityCheck(Sema &S, DelayedDiagnostic &DD, DD.Triggered = true; DoEmitAvailabilityWarning( - S, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityDecl(), - DD.getAvailabilityMessage(), DD.Loc, DD.getUnknownObjCClass(), - DD.getObjCProperty(), false); + S, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(), + DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(), DD.Loc, + DD.getUnknownObjCClass(), DD.getObjCProperty(), false); } void Sema::PopParsingDeclaration(ParsingDeclState state, Decl *decl) { @@ -6825,27 +7264,93 @@ void Sema::redelayDiagnostics(DelayedDiagnosticPool &pool) { curPool->steal(pool); } -void Sema::EmitAvailabilityWarning(AvailabilityResult AR, - NamedDecl *D, StringRef Message, - SourceLocation Loc, - const ObjCInterfaceDecl *UnknownObjCClass, - const ObjCPropertyDecl *ObjCProperty, - bool ObjCPropertyAccess) { +static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR, + const NamedDecl *ReferringDecl, + const NamedDecl *OffendingDecl, + StringRef Message, SourceLocation Loc, + const ObjCInterfaceDecl *UnknownObjCClass, + const ObjCPropertyDecl *ObjCProperty, + bool ObjCPropertyAccess) { // Delay if we're currently parsing a declaration. - if (DelayedDiagnostics.shouldDelayDiagnostics()) { - DelayedDiagnostics.add(DelayedDiagnostic::makeAvailability( - AR, Loc, D, UnknownObjCClass, ObjCProperty, Message, - ObjCPropertyAccess)); + if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { + S.DelayedDiagnostics.add( + DelayedDiagnostic::makeAvailability( + AR, Loc, ReferringDecl, OffendingDecl, UnknownObjCClass, + ObjCProperty, Message, ObjCPropertyAccess)); return; } - Decl *Ctx = cast<Decl>(getCurLexicalContext()); - DoEmitAvailabilityWarning(*this, AR, Ctx, D, Message, Loc, UnknownObjCClass, - ObjCProperty, ObjCPropertyAccess); + Decl *Ctx = cast<Decl>(S.getCurLexicalContext()); + DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl, + Message, Loc, UnknownObjCClass, ObjCProperty, + ObjCPropertyAccess); } namespace { +/// Returns true if the given statement can be a body-like child of \p Parent. +bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) { + switch (Parent->getStmtClass()) { + case Stmt::IfStmtClass: + return cast<IfStmt>(Parent)->getThen() == S || + cast<IfStmt>(Parent)->getElse() == S; + case Stmt::WhileStmtClass: + return cast<WhileStmt>(Parent)->getBody() == S; + case Stmt::DoStmtClass: + return cast<DoStmt>(Parent)->getBody() == S; + case Stmt::ForStmtClass: + return cast<ForStmt>(Parent)->getBody() == S; + case Stmt::CXXForRangeStmtClass: + return cast<CXXForRangeStmt>(Parent)->getBody() == S; + case Stmt::ObjCForCollectionStmtClass: + return cast<ObjCForCollectionStmt>(Parent)->getBody() == S; + case Stmt::CaseStmtClass: + case Stmt::DefaultStmtClass: + return cast<SwitchCase>(Parent)->getSubStmt() == S; + default: + return false; + } +} + +class StmtUSEFinder : public RecursiveASTVisitor<StmtUSEFinder> { + const Stmt *Target; + +public: + bool VisitStmt(Stmt *S) { return S != Target; } + + /// Returns true if the given statement is present in the given declaration. + static bool isContained(const Stmt *Target, const Decl *D) { + StmtUSEFinder Visitor; + Visitor.Target = Target; + return !Visitor.TraverseDecl(const_cast<Decl *>(D)); + } +}; + +/// Traverses the AST and finds the last statement that used a given +/// declaration. +class LastDeclUSEFinder : public RecursiveASTVisitor<LastDeclUSEFinder> { + const Decl *D; + +public: + bool VisitDeclRefExpr(DeclRefExpr *DRE) { + if (DRE->getDecl() == D) + return false; + return true; + } + + static const Stmt *findLastStmtThatUsesDecl(const Decl *D, + const CompoundStmt *Scope) { + LastDeclUSEFinder Visitor; + Visitor.D = D; + for (auto I = Scope->body_rbegin(), E = Scope->body_rend(); I != E; ++I) { + const Stmt *S = *I; + if (!Visitor.TraverseStmt(const_cast<Stmt *>(S))) + return S; + } + return nullptr; + } +}; + /// \brief This class implements -Wunguarded-availability. /// /// This is done with a traversal of the AST of a function that makes reference @@ -6861,6 +7366,7 @@ class DiagnoseUnguardedAvailability /// Stack of potentially nested 'if (@available(...))'s. SmallVector<VersionTuple, 8> AvailabilityStack; + SmallVector<const Stmt *, 16> StmtStack; void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range); @@ -6871,10 +7377,34 @@ public: SemaRef.Context.getTargetInfo().getPlatformMinVersion()); } + bool TraverseDecl(Decl *D) { + // Avoid visiting nested functions to prevent duplicate warnings. + if (!D || isa<FunctionDecl>(D)) + return true; + return Base::TraverseDecl(D); + } + + bool TraverseStmt(Stmt *S) { + if (!S) + return true; + StmtStack.push_back(S); + bool Result = Base::TraverseStmt(S); + StmtStack.pop_back(); + return Result; + } + void IssueDiagnostics(Stmt *S) { TraverseStmt(S); } bool TraverseIfStmt(IfStmt *If); + bool TraverseLambdaExpr(LambdaExpr *E) { return true; } + + bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) { + if (PRE->isClassReceiver()) + DiagnoseDeclAvailability(PRE->getClassReceiver(), PRE->getReceiverLocation()); + return true; + } + bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) { if (ObjCMethodDecl *D = Msg->getMethodDecl()) DiagnoseDeclAvailability( @@ -6894,24 +7424,32 @@ public: return true; } + bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) { + SemaRef.Diag(E->getLocStart(), diag::warn_at_available_unchecked_use) + << (!SemaRef.getLangOpts().ObjC1); + return true; + } + bool VisitTypeLoc(TypeLoc Ty); }; void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( NamedDecl *D, SourceRange Range) { - - VersionTuple ContextVersion = AvailabilityStack.back(); - if (AvailabilityResult Result = - SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr)) { + AvailabilityResult Result; + const NamedDecl *OffendingDecl; + std::tie(Result, OffendingDecl) = + ShouldDiagnoseAvailabilityOfDecl(D, nullptr); + if (Result != AR_Available) { // All other diagnostic kinds have already been handled in // DiagnoseAvailabilityOfDecl. if (Result != AR_NotYetIntroduced) return; - const AvailabilityAttr *AA = getAttrForPlatform(SemaRef.getASTContext(), D); + const AvailabilityAttr *AA = + getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl); VersionTuple Introduced = AA->getIntroduced(); - if (ContextVersion >= Introduced) + if (AvailabilityStack.back() >= Introduced) return; // If the context of this function is less available than D, we should not @@ -6919,18 +7457,96 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx)) return; - SemaRef.Diag(Range.getBegin(), diag::warn_unguarded_availability) + // 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; + + SemaRef.Diag(Range.getBegin(), DiagKind) << Range << D << AvailabilityAttr::getPrettyPlatformName( SemaRef.getASTContext().getTargetInfo().getPlatformName()) << Introduced.getAsString(); - SemaRef.Diag(D->getLocation(), diag::note_availability_specified_here) - << D << /* partial */ 3; + SemaRef.Diag(OffendingDecl->getLocation(), + diag::note_availability_specified_here) + << OffendingDecl << /* partial */ 3; + + auto FixitDiag = + SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) + << Range << D + << (SemaRef.getLangOpts().ObjC1 ? /*@available*/ 0 + : /*__builtin_available*/ 1); + + // Find the statement which should be enclosed in the if @available check. + if (StmtStack.empty()) + return; + const Stmt *StmtOfUse = StmtStack.back(); + const CompoundStmt *Scope = nullptr; + for (const Stmt *S : llvm::reverse(StmtStack)) { + if (const auto *CS = dyn_cast<CompoundStmt>(S)) { + Scope = CS; + break; + } + if (isBodyLikeChildStmt(StmtOfUse, S)) { + // The declaration won't be seen outside of the statement, so we don't + // have to wrap the uses of any declared variables in if (@available). + // Therefore we can avoid setting Scope here. + break; + } + StmtOfUse = S; + } + const Stmt *LastStmtOfUse = nullptr; + if (isa<DeclStmt>(StmtOfUse) && Scope) { + for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) { + if (StmtUSEFinder::isContained(StmtStack.back(), D)) { + LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope); + break; + } + } + } + + const SourceManager &SM = SemaRef.getSourceManager(); + SourceLocation IfInsertionLoc = + SM.getExpansionLoc(StmtOfUse->getLocStart()); + SourceLocation StmtEndLoc = + SM.getExpansionRange( + (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getLocEnd()) + .second; + if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc)) + return; - // FIXME: Replace this with a fixit diagnostic. - SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) - << Range << D; + StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM); + const char *ExtraIndentation = " "; + std::string FixItString; + llvm::raw_string_ostream FixItOS(FixItString); + FixItOS << "if (" << (SemaRef.getLangOpts().ObjC1 ? "@available" + : "__builtin_available") + << "(" + << AvailabilityAttr::getPlatformNameSourceSpelling( + SemaRef.getASTContext().getTargetInfo().getPlatformName()) + << " " << Introduced.getAsString() << ", *)) {\n" + << Indentation << ExtraIndentation; + FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str()); + SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken( + StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(), + /*SkipTrailingWhitespaceAndNewLine=*/false); + if (ElseInsertionLoc.isInvalid()) + ElseInsertionLoc = + Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts()); + FixItOS.str().clear(); + FixItOS << "\n" + << Indentation << "} else {\n" + << Indentation << ExtraIndentation + << "// Fallback on earlier versions\n" + << Indentation << "}"; + FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str()); } } @@ -6938,6 +7554,9 @@ bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { const Type *TyPtr = Ty.getTypePtr(); SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()}; + if (Range.isInvalid()) + return true; + if (const TagType *TT = dyn_cast<TagType>(TyPtr)) { TagDecl *TD = TT->getDecl(); DiagnoseDeclAvailability(TD, Range); @@ -6990,8 +7609,51 @@ void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { Body = FD->getBody(); } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) Body = MD->getBody(); + else if (auto *BD = dyn_cast<BlockDecl>(D)) + Body = BD->getBody(); assert(Body && "Need a body here!"); DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body); } + +void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, SourceLocation Loc, + const ObjCInterfaceDecl *UnknownObjCClass, + bool ObjCPropertyAccess, + bool AvoidPartialAvailabilityChecks) { + std::string Message; + AvailabilityResult Result; + const NamedDecl* OffendingDecl; + // See if this declaration is unavailable, deprecated, or partial. + std::tie(Result, OffendingDecl) = ShouldDiagnoseAvailabilityOfDecl(D, &Message); + if (Result == AR_Available) + return; + + if (Result == AR_NotYetIntroduced) { + if (AvoidPartialAvailabilityChecks) + return; + + // We need to know the @available context in the current function to + // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that + // when we're done parsing the current function. + if (getCurFunctionOrMethodDecl()) { + getEnclosingFunction()->HasPotentialAvailabilityViolations = true; + return; + } else if (getCurBlock() || getCurLambda()) { + getCurFunction()->HasPotentialAvailabilityViolations = true; + return; + } + } + + const ObjCPropertyDecl *ObjCPDecl = nullptr; + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { + AvailabilityResult PDeclResult = PD->getAvailability(nullptr); + if (PDeclResult == Result) + ObjCPDecl = PD; + } + } + + EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Loc, + UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); +} |