| //===--- SemaDeclAttr.cpp - Declaration Attribute Handling ----------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements decl-related attribute processing. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/AST/ASTConsumer.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/ASTMutationListener.h" |
| #include "clang/AST/CXXInheritance.h" |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/DeclObjC.h" |
| #include "clang/AST/DeclTemplate.h" |
| #include "clang/AST/Expr.h" |
| #include "clang/AST/ExprCXX.h" |
| #include "clang/AST/Mangle.h" |
| #include "clang/AST/RecursiveASTVisitor.h" |
| #include "clang/AST/Type.h" |
| #include "clang/Basic/CharInfo.h" |
| #include "clang/Basic/DarwinSDKInfo.h" |
| #include "clang/Basic/SourceLocation.h" |
| #include "clang/Basic/SourceManager.h" |
| #include "clang/Basic/TargetBuiltins.h" |
| #include "clang/Basic/TargetInfo.h" |
| #include "clang/Lex/Preprocessor.h" |
| #include "clang/Sema/DeclSpec.h" |
| #include "clang/Sema/DelayedDiagnostic.h" |
| #include "clang/Sema/Initialization.h" |
| #include "clang/Sema/Lookup.h" |
| #include "clang/Sema/ParsedAttr.h" |
| #include "clang/Sema/Scope.h" |
| #include "clang/Sema/ScopeInfo.h" |
| #include "clang/Sema/SemaInternal.h" |
| #include "llvm/ADT/Optional.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/IR/Assumptions.h" |
| #include "llvm/MC/MCSectionMachO.h" |
| #include "llvm/Support/Error.h" |
| #include "llvm/Support/MathExtras.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| using namespace clang; |
| using namespace sema; |
| |
| namespace AttributeLangSupport { |
| enum LANG { |
| C, |
| Cpp, |
| ObjC |
| }; |
| } // end namespace AttributeLangSupport |
| |
| //===----------------------------------------------------------------------===// |
| // Helper functions |
| //===----------------------------------------------------------------------===// |
| |
| /// isFunctionOrMethod - Return true if the given decl has function |
| /// type (function or function-typed variable) or an Objective-C |
| /// method. |
| static bool isFunctionOrMethod(const Decl *D) { |
| return (D->getFunctionType() != nullptr) || isa<ObjCMethodDecl>(D); |
| } |
| |
| /// Return true if the given decl has function type (function or |
| /// function-typed variable) or an Objective-C method or a block. |
| static bool isFunctionOrMethodOrBlock(const Decl *D) { |
| return isFunctionOrMethod(D) || isa<BlockDecl>(D); |
| } |
| |
| /// Return true if the given decl has a declarator that should have |
| /// been processed by Sema::GetTypeForDeclarator. |
| static bool hasDeclarator(const Decl *D) { |
| // In some sense, TypedefDecl really *ought* to be a DeclaratorDecl. |
| return isa<DeclaratorDecl>(D) || isa<BlockDecl>(D) || isa<TypedefNameDecl>(D) || |
| isa<ObjCPropertyDecl>(D); |
| } |
| |
| /// hasFunctionProto - Return true if the given decl has a argument |
| /// information. This decl should have already passed |
| /// isFunctionOrMethod or isFunctionOrMethodOrBlock. |
| static bool hasFunctionProto(const Decl *D) { |
| if (const FunctionType *FnTy = D->getFunctionType()) |
| return isa<FunctionProtoType>(FnTy); |
| return isa<ObjCMethodDecl>(D) || isa<BlockDecl>(D); |
| } |
| |
| /// getFunctionOrMethodNumParams - Return number of function or method |
| /// parameters. It is an error to call this on a K&R function (use |
| /// hasFunctionProto first). |
| static unsigned getFunctionOrMethodNumParams(const Decl *D) { |
| if (const FunctionType *FnTy = D->getFunctionType()) |
| return cast<FunctionProtoType>(FnTy)->getNumParams(); |
| if (const auto *BD = dyn_cast<BlockDecl>(D)) |
| return BD->getNumParams(); |
| return cast<ObjCMethodDecl>(D)->param_size(); |
| } |
| |
| static const ParmVarDecl *getFunctionOrMethodParam(const Decl *D, |
| unsigned Idx) { |
| if (const auto *FD = dyn_cast<FunctionDecl>(D)) |
| return FD->getParamDecl(Idx); |
| if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) |
| return MD->getParamDecl(Idx); |
| if (const auto *BD = dyn_cast<BlockDecl>(D)) |
| return BD->getParamDecl(Idx); |
| return nullptr; |
| } |
| |
| static QualType getFunctionOrMethodParamType(const Decl *D, unsigned Idx) { |
| if (const FunctionType *FnTy = D->getFunctionType()) |
| return cast<FunctionProtoType>(FnTy)->getParamType(Idx); |
| if (const auto *BD = dyn_cast<BlockDecl>(D)) |
| return BD->getParamDecl(Idx)->getType(); |
| |
| return cast<ObjCMethodDecl>(D)->parameters()[Idx]->getType(); |
| } |
| |
| static SourceRange getFunctionOrMethodParamRange(const Decl *D, unsigned Idx) { |
| if (auto *PVD = getFunctionOrMethodParam(D, Idx)) |
| return PVD->getSourceRange(); |
| return SourceRange(); |
| } |
| |
| static QualType getFunctionOrMethodResultType(const Decl *D) { |
| if (const FunctionType *FnTy = D->getFunctionType()) |
| return FnTy->getReturnType(); |
| return cast<ObjCMethodDecl>(D)->getReturnType(); |
| } |
| |
| static SourceRange getFunctionOrMethodResultSourceRange(const Decl *D) { |
| if (const auto *FD = dyn_cast<FunctionDecl>(D)) |
| return FD->getReturnTypeSourceRange(); |
| if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) |
| return MD->getReturnTypeSourceRange(); |
| return SourceRange(); |
| } |
| |
| static bool isFunctionOrMethodVariadic(const Decl *D) { |
| if (const FunctionType *FnTy = D->getFunctionType()) |
| return cast<FunctionProtoType>(FnTy)->isVariadic(); |
| if (const auto *BD = dyn_cast<BlockDecl>(D)) |
| return BD->isVariadic(); |
| return cast<ObjCMethodDecl>(D)->isVariadic(); |
| } |
| |
| static bool isInstanceMethod(const Decl *D) { |
| if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(D)) |
| return MethodDecl->isInstance(); |
| return false; |
| } |
| |
| static inline bool isNSStringType(QualType T, ASTContext &Ctx, |
| bool AllowNSAttributedString = false) { |
| const auto *PT = T->getAs<ObjCObjectPointerType>(); |
| if (!PT) |
| return false; |
| |
| ObjCInterfaceDecl *Cls = PT->getObjectType()->getInterface(); |
| if (!Cls) |
| return false; |
| |
| IdentifierInfo* ClsName = Cls->getIdentifier(); |
| |
| if (AllowNSAttributedString && |
| ClsName == &Ctx.Idents.get("NSAttributedString")) |
| return true; |
| // FIXME: Should we walk the chain of classes? |
| return ClsName == &Ctx.Idents.get("NSString") || |
| ClsName == &Ctx.Idents.get("NSMutableString"); |
| } |
| |
| static inline bool isCFStringType(QualType T, ASTContext &Ctx) { |
| const auto *PT = T->getAs<PointerType>(); |
| if (!PT) |
| return false; |
| |
| const auto *RT = PT->getPointeeType()->getAs<RecordType>(); |
| if (!RT) |
| return false; |
| |
| const RecordDecl *RD = RT->getDecl(); |
| if (RD->getTagKind() != TTK_Struct) |
| return false; |
| |
| return RD->getIdentifier() == &Ctx.Idents.get("__CFString"); |
| } |
| |
| static unsigned getNumAttributeArgs(const ParsedAttr &AL) { |
| // FIXME: Include the type in the argument list. |
| return AL.getNumArgs() + AL.hasParsedType(); |
| } |
| |
| /// A helper function to provide Attribute Location for the Attr types |
| /// AND the ParsedAttr. |
| template <typename AttrInfo> |
| static std::enable_if_t<std::is_base_of<Attr, AttrInfo>::value, SourceLocation> |
| getAttrLoc(const AttrInfo &AL) { |
| return AL.getLocation(); |
| } |
| static SourceLocation getAttrLoc(const ParsedAttr &AL) { return AL.getLoc(); } |
| |
| /// If Expr is a valid integer constant, get the value of the integer |
| /// expression and return success or failure. May output an error. |
| /// |
| /// Negative argument is implicitly converted to unsigned, unless |
| /// \p StrictlyUnsigned is true. |
| template <typename AttrInfo> |
| static bool checkUInt32Argument(Sema &S, const AttrInfo &AI, const Expr *Expr, |
| uint32_t &Val, unsigned Idx = UINT_MAX, |
| bool StrictlyUnsigned = false) { |
| Optional<llvm::APSInt> I = llvm::APSInt(32); |
| if (Expr->isTypeDependent() || |
| !(I = Expr->getIntegerConstantExpr(S.Context))) { |
| if (Idx != UINT_MAX) |
| S.Diag(getAttrLoc(AI), diag::err_attribute_argument_n_type) |
| << &AI << Idx << AANT_ArgumentIntegerConstant |
| << Expr->getSourceRange(); |
| else |
| S.Diag(getAttrLoc(AI), diag::err_attribute_argument_type) |
| << &AI << AANT_ArgumentIntegerConstant << Expr->getSourceRange(); |
| return false; |
| } |
| |
| if (!I->isIntN(32)) { |
| S.Diag(Expr->getExprLoc(), diag::err_ice_too_large) |
| << toString(*I, 10, false) << 32 << /* Unsigned */ 1; |
| return false; |
| } |
| |
| if (StrictlyUnsigned && I->isSigned() && I->isNegative()) { |
| S.Diag(getAttrLoc(AI), diag::err_attribute_requires_positive_integer) |
| << &AI << /*non-negative*/ 1; |
| return false; |
| } |
| |
| Val = (uint32_t)I->getZExtValue(); |
| return true; |
| } |
| |
| /// 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. |
| template <typename AttrInfo> |
| static bool checkPositiveIntArgument(Sema &S, const AttrInfo &AI, const Expr *Expr, |
| int &Val, unsigned Idx = UINT_MAX) { |
| uint32_t UVal; |
| if (!checkUInt32Argument(S, AI, Expr, UVal, Idx)) |
| return false; |
| |
| if (UVal > (uint32_t)std::numeric_limits<int>::max()) { |
| llvm::APSInt I(32); // for toString |
| I = UVal; |
| S.Diag(Expr->getExprLoc(), diag::err_ice_too_large) |
| << toString(I, 10, false) << 32 << /* Unsigned */ 0; |
| return false; |
| } |
| |
| Val = UVal; |
| return true; |
| } |
| |
| /// Diagnose mutually exclusive attributes when present on a given |
| /// declaration. Returns true if diagnosed. |
| template <typename AttrTy> |
| static bool checkAttrMutualExclusion(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (const auto *A = D->getAttr<AttrTy>()) { |
| S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible) << AL << A; |
| S.Diag(A->getLocation(), diag::note_conflicting_attribute); |
| return true; |
| } |
| return false; |
| } |
| |
| template <typename AttrTy> |
| static bool checkAttrMutualExclusion(Sema &S, Decl *D, const Attr &AL) { |
| if (const auto *A = D->getAttr<AttrTy>()) { |
| S.Diag(AL.getLocation(), diag::err_attributes_are_not_compatible) << &AL |
| << A; |
| S.Diag(A->getLocation(), diag::note_conflicting_attribute); |
| return true; |
| } |
| return false; |
| } |
| |
| /// Check if IdxExpr is a valid parameter index for a function or |
| /// instance method D. May output an error. |
| /// |
| /// \returns true if IdxExpr is a valid index. |
| template <typename AttrInfo> |
| static bool checkFunctionOrMethodParameterIndex( |
| Sema &S, const Decl *D, const AttrInfo &AI, unsigned AttrArgNum, |
| const Expr *IdxExpr, ParamIdx &Idx, bool CanIndexImplicitThis = false) { |
| assert(isFunctionOrMethodOrBlock(D)); |
| |
| // In C++ the implicit 'this' function parameter also counts. |
| // Parameters are counted from one. |
| bool HP = hasFunctionProto(D); |
| bool HasImplicitThisParam = isInstanceMethod(D); |
| bool IV = HP && isFunctionOrMethodVariadic(D); |
| unsigned NumParams = |
| (HP ? getFunctionOrMethodNumParams(D) : 0) + HasImplicitThisParam; |
| |
| Optional<llvm::APSInt> IdxInt; |
| if (IdxExpr->isTypeDependent() || |
| !(IdxInt = IdxExpr->getIntegerConstantExpr(S.Context))) { |
| S.Diag(getAttrLoc(AI), diag::err_attribute_argument_n_type) |
| << &AI << AttrArgNum << AANT_ArgumentIntegerConstant |
| << IdxExpr->getSourceRange(); |
| return false; |
| } |
| |
| unsigned IdxSource = IdxInt->getLimitedValue(UINT_MAX); |
| if (IdxSource < 1 || (!IV && IdxSource > NumParams)) { |
| S.Diag(getAttrLoc(AI), diag::err_attribute_argument_out_of_bounds) |
| << &AI << AttrArgNum << IdxExpr->getSourceRange(); |
| return false; |
| } |
| if (HasImplicitThisParam && !CanIndexImplicitThis) { |
| if (IdxSource == 1) { |
| S.Diag(getAttrLoc(AI), diag::err_attribute_invalid_implicit_this_argument) |
| << &AI << IdxExpr->getSourceRange(); |
| return false; |
| } |
| } |
| |
| Idx = ParamIdx(IdxSource, D); |
| return true; |
| } |
| |
| /// Check if the argument \p ArgNum of \p Attr is a ASCII string literal. |
| /// If not emit an error and return false. If the argument is an identifier it |
| /// will emit an error with a fixit hint and treat it as if it was a string |
| /// literal. |
| bool Sema::checkStringLiteralArgumentAttr(const ParsedAttr &AL, unsigned ArgNum, |
| StringRef &Str, |
| SourceLocation *ArgLocation) { |
| // Look for identifiers. If we have one emit a hint to fix it to a literal. |
| if (AL.isArgIdent(ArgNum)) { |
| IdentifierLoc *Loc = AL.getArgAsIdent(ArgNum); |
| Diag(Loc->Loc, diag::err_attribute_argument_type) |
| << AL << AANT_ArgumentString |
| << FixItHint::CreateInsertion(Loc->Loc, "\"") |
| << FixItHint::CreateInsertion(getLocForEndOfToken(Loc->Loc), "\""); |
| Str = Loc->Ident->getName(); |
| if (ArgLocation) |
| *ArgLocation = Loc->Loc; |
| return true; |
| } |
| |
| // Now check for an actual string literal. |
| Expr *ArgExpr = AL.getArgAsExpr(ArgNum); |
| const auto *Literal = dyn_cast<StringLiteral>(ArgExpr->IgnoreParenCasts()); |
| if (ArgLocation) |
| *ArgLocation = ArgExpr->getBeginLoc(); |
| |
| if (!Literal || !Literal->isAscii()) { |
| Diag(ArgExpr->getBeginLoc(), diag::err_attribute_argument_type) |
| << AL << AANT_ArgumentString; |
| return false; |
| } |
| |
| Str = Literal->getString(); |
| return true; |
| } |
| |
| /// Applies the given attribute to the Decl without performing any |
| /// additional semantic checking. |
| template <typename AttrType> |
| static void handleSimpleAttribute(Sema &S, Decl *D, |
| const AttributeCommonInfo &CI) { |
| D->addAttr(::new (S.Context) AttrType(S.Context, CI)); |
| } |
| |
| template <typename... DiagnosticArgs> |
| static const Sema::SemaDiagnosticBuilder& |
| appendDiagnostics(const Sema::SemaDiagnosticBuilder &Bldr) { |
| return Bldr; |
| } |
| |
| template <typename T, typename... DiagnosticArgs> |
| static const Sema::SemaDiagnosticBuilder& |
| appendDiagnostics(const Sema::SemaDiagnosticBuilder &Bldr, T &&ExtraArg, |
| DiagnosticArgs &&... ExtraArgs) { |
| return appendDiagnostics(Bldr << std::forward<T>(ExtraArg), |
| std::forward<DiagnosticArgs>(ExtraArgs)...); |
| } |
| |
| /// Add an attribute @c AttrType to declaration @c D, provided that |
| /// @c PassesCheck is true. |
| /// Otherwise, emit diagnostic @c DiagID, passing in all parameters |
| /// specified in @c ExtraArgs. |
| template <typename AttrType, typename... DiagnosticArgs> |
| static void handleSimpleAttributeOrDiagnose(Sema &S, Decl *D, |
| const AttributeCommonInfo &CI, |
| bool PassesCheck, unsigned DiagID, |
| DiagnosticArgs &&... ExtraArgs) { |
| if (!PassesCheck) { |
| Sema::SemaDiagnosticBuilder DB = S.Diag(D->getBeginLoc(), DiagID); |
| appendDiagnostics(DB, std::forward<DiagnosticArgs>(ExtraArgs)...); |
| return; |
| } |
| handleSimpleAttribute<AttrType>(S, D, CI); |
| } |
| |
| /// Check if the passed-in expression is of type int or bool. |
| static bool isIntOrBool(Expr *Exp) { |
| QualType QT = Exp->getType(); |
| return QT->isBooleanType() || QT->isIntegerType(); |
| } |
| |
| |
| // Check to see if the type is a smart pointer of some kind. We assume |
| // it's a smart pointer if it defines both operator-> and operator*. |
| static bool threadSafetyCheckIsSmartPointer(Sema &S, const RecordType* RT) { |
| auto IsOverloadedOperatorPresent = [&S](const RecordDecl *Record, |
| OverloadedOperatorKind Op) { |
| DeclContextLookupResult Result = |
| Record->lookup(S.Context.DeclarationNames.getCXXOperatorName(Op)); |
| return !Result.empty(); |
| }; |
| |
| const RecordDecl *Record = RT->getDecl(); |
| bool foundStarOperator = IsOverloadedOperatorPresent(Record, OO_Star); |
| bool foundArrowOperator = IsOverloadedOperatorPresent(Record, OO_Arrow); |
| if (foundStarOperator && foundArrowOperator) |
| return true; |
| |
| const CXXRecordDecl *CXXRecord = dyn_cast<CXXRecordDecl>(Record); |
| if (!CXXRecord) |
| return false; |
| |
| for (auto BaseSpecifier : CXXRecord->bases()) { |
| if (!foundStarOperator) |
| foundStarOperator = IsOverloadedOperatorPresent( |
| BaseSpecifier.getType()->getAsRecordDecl(), OO_Star); |
| if (!foundArrowOperator) |
| foundArrowOperator = IsOverloadedOperatorPresent( |
| BaseSpecifier.getType()->getAsRecordDecl(), OO_Arrow); |
| } |
| |
| if (foundStarOperator && foundArrowOperator) |
| return true; |
| |
| return false; |
| } |
| |
| /// Check if passed in Decl is a pointer type. |
| /// Note that this function may produce an error message. |
| /// \return true if the Decl is a pointer type; false otherwise |
| static bool threadSafetyCheckIsPointer(Sema &S, const Decl *D, |
| const ParsedAttr &AL) { |
| const auto *VD = cast<ValueDecl>(D); |
| QualType QT = VD->getType(); |
| if (QT->isAnyPointerType()) |
| return true; |
| |
| if (const auto *RT = QT->getAs<RecordType>()) { |
| // If it's an incomplete type, it could be a smart pointer; skip it. |
| // (We don't want to force template instantiation if we can avoid it, |
| // since that would alter the order in which templates are instantiated.) |
| if (RT->isIncompleteType()) |
| return true; |
| |
| if (threadSafetyCheckIsSmartPointer(S, RT)) |
| return true; |
| } |
| |
| S.Diag(AL.getLoc(), diag::warn_thread_attribute_decl_not_pointer) << AL << QT; |
| return false; |
| } |
| |
| /// Checks that the passed in QualType either is of RecordType or points |
| /// to RecordType. Returns the relevant RecordType, null if it does not exit. |
| static const RecordType *getRecordType(QualType QT) { |
| if (const auto *RT = QT->getAs<RecordType>()) |
| return RT; |
| |
| // Now check if we point to record type. |
| if (const auto *PT = QT->getAs<PointerType>()) |
| return PT->getPointeeType()->getAs<RecordType>(); |
| |
| return nullptr; |
| } |
| |
| template <typename AttrType> |
| static bool checkRecordDeclForAttr(const RecordDecl *RD) { |
| // Check if the record itself has the attribute. |
| if (RD->hasAttr<AttrType>()) |
| return true; |
| |
| // Else check if any base classes have the attribute. |
| if (const auto *CRD = dyn_cast<CXXRecordDecl>(RD)) { |
| if (!CRD->forallBases([](const CXXRecordDecl *Base) { |
| return !Base->hasAttr<AttrType>(); |
| })) |
| return true; |
| } |
| return false; |
| } |
| |
| static bool checkRecordTypeForCapability(Sema &S, QualType Ty) { |
| const RecordType *RT = getRecordType(Ty); |
| |
| if (!RT) |
| return false; |
| |
| // Don't check for the capability if the class hasn't been defined yet. |
| if (RT->isIncompleteType()) |
| return true; |
| |
| // Allow smart pointers to be used as capability objects. |
| // FIXME -- Check the type that the smart pointer points to. |
| if (threadSafetyCheckIsSmartPointer(S, RT)) |
| return true; |
| |
| return checkRecordDeclForAttr<CapabilityAttr>(RT->getDecl()); |
| } |
| |
| static bool checkTypedefTypeForCapability(QualType Ty) { |
| const auto *TD = Ty->getAs<TypedefType>(); |
| if (!TD) |
| return false; |
| |
| TypedefNameDecl *TN = TD->getDecl(); |
| if (!TN) |
| return false; |
| |
| return TN->hasAttr<CapabilityAttr>(); |
| } |
| |
| static bool typeHasCapability(Sema &S, QualType Ty) { |
| if (checkTypedefTypeForCapability(Ty)) |
| return true; |
| |
| if (checkRecordTypeForCapability(S, Ty)) |
| return true; |
| |
| return false; |
| } |
| |
| static bool isCapabilityExpr(Sema &S, const Expr *Ex) { |
| // Capability expressions are simple expressions involving the boolean logic |
| // operators &&, || or !, a simple DeclRefExpr, CastExpr or a ParenExpr. Once |
| // a DeclRefExpr is found, its type should be checked to determine whether it |
| // is a capability or not. |
| |
| if (const auto *E = dyn_cast<CastExpr>(Ex)) |
| return isCapabilityExpr(S, E->getSubExpr()); |
| else if (const auto *E = dyn_cast<ParenExpr>(Ex)) |
| return isCapabilityExpr(S, E->getSubExpr()); |
| else if (const auto *E = dyn_cast<UnaryOperator>(Ex)) { |
| if (E->getOpcode() == UO_LNot || E->getOpcode() == UO_AddrOf || |
| E->getOpcode() == UO_Deref) |
| return isCapabilityExpr(S, E->getSubExpr()); |
| return false; |
| } else if (const auto *E = dyn_cast<BinaryOperator>(Ex)) { |
| if (E->getOpcode() == BO_LAnd || E->getOpcode() == BO_LOr) |
| return isCapabilityExpr(S, E->getLHS()) && |
| isCapabilityExpr(S, E->getRHS()); |
| return false; |
| } |
| |
| return typeHasCapability(S, Ex->getType()); |
| } |
| |
| /// Checks that all attribute arguments, starting from Sidx, resolve to |
| /// a capability object. |
| /// \param Sidx The attribute argument index to start checking with. |
| /// \param ParamIdxOk Whether an argument can be indexing into a function |
| /// parameter list. |
| static void checkAttrArgsAreCapabilityObjs(Sema &S, Decl *D, |
| const ParsedAttr &AL, |
| SmallVectorImpl<Expr *> &Args, |
| unsigned Sidx = 0, |
| bool ParamIdxOk = false) { |
| if (Sidx == AL.getNumArgs()) { |
| // If we don't have any capability arguments, the attribute implicitly |
| // refers to 'this'. So we need to make sure that 'this' exists, i.e. we're |
| // a non-static method, and that the class is a (scoped) capability. |
| const auto *MD = dyn_cast<const CXXMethodDecl>(D); |
| if (MD && !MD->isStatic()) { |
| const CXXRecordDecl *RD = MD->getParent(); |
| // FIXME -- need to check this again on template instantiation |
| if (!checkRecordDeclForAttr<CapabilityAttr>(RD) && |
| !checkRecordDeclForAttr<ScopedLockableAttr>(RD)) |
| S.Diag(AL.getLoc(), |
| diag::warn_thread_attribute_not_on_capability_member) |
| << AL << MD->getParent(); |
| } else { |
| S.Diag(AL.getLoc(), diag::warn_thread_attribute_not_on_non_static_member) |
| << AL; |
| } |
| } |
| |
| for (unsigned Idx = Sidx; Idx < AL.getNumArgs(); ++Idx) { |
| Expr *ArgExp = AL.getArgAsExpr(Idx); |
| |
| if (ArgExp->isTypeDependent()) { |
| // FIXME -- need to check this again on template instantiation |
| Args.push_back(ArgExp); |
| continue; |
| } |
| |
| if (const auto *StrLit = dyn_cast<StringLiteral>(ArgExp)) { |
| if (StrLit->getLength() == 0 || |
| (StrLit->isAscii() && StrLit->getString() == StringRef("*"))) { |
| // Pass empty strings to the analyzer without warnings. |
| // Treat "*" as the universal lock. |
| Args.push_back(ArgExp); |
| continue; |
| } |
| |
| // We allow constant strings to be used as a placeholder for expressions |
| // that are not valid C++ syntax, but warn that they are ignored. |
| S.Diag(AL.getLoc(), diag::warn_thread_attribute_ignored) << AL; |
| Args.push_back(ArgExp); |
| continue; |
| } |
| |
| QualType ArgTy = ArgExp->getType(); |
| |
| // A pointer to member expression of the form &MyClass::mu is treated |
| // specially -- we need to look at the type of the member. |
| if (const auto *UOp = dyn_cast<UnaryOperator>(ArgExp)) |
| if (UOp->getOpcode() == UO_AddrOf) |
| if (const auto *DRE = dyn_cast<DeclRefExpr>(UOp->getSubExpr())) |
| if (DRE->getDecl()->isCXXInstanceMember()) |
| ArgTy = DRE->getDecl()->getType(); |
| |
| // First see if we can just cast to record type, or pointer to record type. |
| const RecordType *RT = getRecordType(ArgTy); |
| |
| // Now check if we index into a record type function param. |
| if(!RT && ParamIdxOk) { |
| const auto *FD = dyn_cast<FunctionDecl>(D); |
| const auto *IL = dyn_cast<IntegerLiteral>(ArgExp); |
| if(FD && IL) { |
| unsigned int NumParams = FD->getNumParams(); |
| llvm::APInt ArgValue = IL->getValue(); |
| uint64_t ParamIdxFromOne = ArgValue.getZExtValue(); |
| uint64_t ParamIdxFromZero = ParamIdxFromOne - 1; |
| if (!ArgValue.isStrictlyPositive() || ParamIdxFromOne > NumParams) { |
| S.Diag(AL.getLoc(), |
| diag::err_attribute_argument_out_of_bounds_extra_info) |
| << AL << Idx + 1 << NumParams; |
| continue; |
| } |
| ArgTy = FD->getParamDecl(ParamIdxFromZero)->getType(); |
| } |
| } |
| |
| // If the type does not have a capability, see if the components of the |
| // expression have capabilities. This allows for writing C code where the |
| // capability may be on the type, and the expression is a capability |
| // boolean logic expression. Eg) requires_capability(A || B && !C) |
| if (!typeHasCapability(S, ArgTy) && !isCapabilityExpr(S, ArgExp)) |
| S.Diag(AL.getLoc(), diag::warn_thread_attribute_argument_not_lockable) |
| << AL << ArgTy; |
| |
| Args.push_back(ArgExp); |
| } |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Attribute Implementations |
| //===----------------------------------------------------------------------===// |
| |
| static void handlePtGuardedVarAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (!threadSafetyCheckIsPointer(S, D, AL)) |
| return; |
| |
| D->addAttr(::new (S.Context) PtGuardedVarAttr(S.Context, AL)); |
| } |
| |
| static bool checkGuardedByAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL, |
| Expr *&Arg) { |
| SmallVector<Expr *, 1> Args; |
| // check that all arguments are lockable objects |
| checkAttrArgsAreCapabilityObjs(S, D, AL, Args); |
| unsigned Size = Args.size(); |
| if (Size != 1) |
| return false; |
| |
| Arg = Args[0]; |
| |
| return true; |
| } |
| |
| static void handleGuardedByAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| Expr *Arg = nullptr; |
| if (!checkGuardedByAttrCommon(S, D, AL, Arg)) |
| return; |
| |
| D->addAttr(::new (S.Context) GuardedByAttr(S.Context, AL, Arg)); |
| } |
| |
| static void handlePtGuardedByAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| Expr *Arg = nullptr; |
| if (!checkGuardedByAttrCommon(S, D, AL, Arg)) |
| return; |
| |
| if (!threadSafetyCheckIsPointer(S, D, AL)) |
| return; |
| |
| D->addAttr(::new (S.Context) PtGuardedByAttr(S.Context, AL, Arg)); |
| } |
| |
| static bool checkAcquireOrderAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL, |
| SmallVectorImpl<Expr *> &Args) { |
| if (!AL.checkAtLeastNumArgs(S, 1)) |
| return false; |
| |
| // Check that this attribute only applies to lockable types. |
| QualType QT = cast<ValueDecl>(D)->getType(); |
| if (!QT->isDependentType() && !typeHasCapability(S, QT)) { |
| S.Diag(AL.getLoc(), diag::warn_thread_attribute_decl_not_lockable) << AL; |
| return false; |
| } |
| |
| // Check that all arguments are lockable objects. |
| checkAttrArgsAreCapabilityObjs(S, D, AL, Args); |
| if (Args.empty()) |
| return false; |
| |
| return true; |
| } |
| |
| static void handleAcquiredAfterAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| SmallVector<Expr *, 1> Args; |
| if (!checkAcquireOrderAttrCommon(S, D, AL, Args)) |
| return; |
| |
| Expr **StartArg = &Args[0]; |
| D->addAttr(::new (S.Context) |
| AcquiredAfterAttr(S.Context, AL, StartArg, Args.size())); |
| } |
| |
| static void handleAcquiredBeforeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| SmallVector<Expr *, 1> Args; |
| if (!checkAcquireOrderAttrCommon(S, D, AL, Args)) |
| return; |
| |
| Expr **StartArg = &Args[0]; |
| D->addAttr(::new (S.Context) |
| AcquiredBeforeAttr(S.Context, AL, StartArg, Args.size())); |
| } |
| |
| static bool checkLockFunAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL, |
| SmallVectorImpl<Expr *> &Args) { |
| // zero or more arguments ok |
| // check that all arguments are lockable objects |
| checkAttrArgsAreCapabilityObjs(S, D, AL, Args, 0, /*ParamIdxOk=*/true); |
| |
| return true; |
| } |
| |
| static void handleAssertSharedLockAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| SmallVector<Expr *, 1> Args; |
| if (!checkLockFunAttrCommon(S, D, AL, Args)) |
| return; |
| |
| unsigned Size = Args.size(); |
| Expr **StartArg = Size == 0 ? nullptr : &Args[0]; |
| D->addAttr(::new (S.Context) |
| AssertSharedLockAttr(S.Context, AL, StartArg, Size)); |
| } |
| |
| static void handleAssertExclusiveLockAttr(Sema &S, Decl *D, |
| const ParsedAttr &AL) { |
| SmallVector<Expr *, 1> Args; |
| if (!checkLockFunAttrCommon(S, D, AL, Args)) |
| return; |
| |
| unsigned Size = Args.size(); |
| Expr **StartArg = Size == 0 ? nullptr : &Args[0]; |
| D->addAttr(::new (S.Context) |
| AssertExclusiveLockAttr(S.Context, AL, StartArg, Size)); |
| } |
| |
| /// 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. |
| /// |
| /// AttrArgNo is used to actually retrieve the argument, so it's base-0. |
| template <typename AttrInfo> |
| static bool checkParamIsIntegerType(Sema &S, const Decl *D, const AttrInfo &AI, |
| unsigned AttrArgNo) { |
| assert(AI.isArgExpr(AttrArgNo) && "Expected expression argument"); |
| Expr *AttrArg = AI.getArgAsExpr(AttrArgNo); |
| ParamIdx Idx; |
| if (!checkFunctionOrMethodParameterIndex(S, D, AI, AttrArgNo + 1, AttrArg, |
| Idx)) |
| return false; |
| |
| QualType ParamTy = getFunctionOrMethodParamType(D, Idx.getASTIndex()); |
| if (!ParamTy->isIntegerType() && !ParamTy->isCharType()) { |
| SourceLocation SrcLoc = AttrArg->getBeginLoc(); |
| S.Diag(SrcLoc, diag::err_attribute_integers_only) |
| << AI << getFunctionOrMethodParamRange(D, Idx.getASTIndex()); |
| return false; |
| } |
| return true; |
| } |
| |
| static void handleAllocSizeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (!AL.checkAtLeastNumArgs(S, 1) || !AL.checkAtMostNumArgs(S, 2)) |
| return; |
| |
| assert(isFunctionOrMethod(D) && hasFunctionProto(D)); |
| |
| QualType RetTy = getFunctionOrMethodResultType(D); |
| if (!RetTy->isPointerType()) { |
| S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only) << AL; |
| return; |
| } |
| |
| const Expr *SizeExpr = AL.getArgAsExpr(0); |
| int SizeArgNoVal; |
| // Parameter indices are 1-indexed, hence Index=1 |
| if (!checkPositiveIntArgument(S, AL, SizeExpr, SizeArgNoVal, /*Idx=*/1)) |
| return; |
| if (!checkParamIsIntegerType(S, D, AL, /*AttrArgNo=*/0)) |
| return; |
| ParamIdx SizeArgNo(SizeArgNoVal, D); |
| |
| ParamIdx NumberArgNo; |
| if (AL.getNumArgs() == 2) { |
| const Expr *NumberExpr = AL.getArgAsExpr(1); |
| int Val; |
| // Parameter indices are 1-based, hence Index=2 |
| if (!checkPositiveIntArgument(S, AL, NumberExpr, Val, /*Idx=*/2)) |
| return; |
| if (!checkParamIsIntegerType(S, D, AL, /*AttrArgNo=*/1)) |
| return; |
| NumberArgNo = ParamIdx(Val, D); |
| } |
| |
| D->addAttr(::new (S.Context) |
| AllocSizeAttr(S.Context, AL, SizeArgNo, NumberArgNo)); |
| } |
| |
| static bool checkTryLockFunAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL, |
| SmallVectorImpl<Expr *> &Args) { |
| if (!AL.checkAtLeastNumArgs(S, 1)) |
| return false; |
| |
| if (!isIntOrBool(AL.getArgAsExpr(0))) { |
| S.Diag(AL.getLoc(), diag::err_attribute_argument_n_type) |
| << AL << 1 << AANT_ArgumentIntOrBool; |
| return false; |
| } |
| |
| // check that all arguments are lockable objects |
| checkAttrArgsAreCapabilityObjs(S, D, AL, Args, 1); |
| |
| return true; |
| } |
| |
| static void handleSharedTrylockFunctionAttr(Sema &S, Decl *D, |
| const ParsedAttr &AL) { |
| SmallVector<Expr*, 2> Args; |
| if (!checkTryLockFunAttrCommon(S, D, AL, Args)) |
| return; |
| |
| D->addAttr(::new (S.Context) SharedTrylockFunctionAttr( |
| S.Context, AL, AL.getArgAsExpr(0), Args.data(), Args.size())); |
| } |
| |
| static void handleExclusiveTrylockFunctionAttr(Sema &S, Decl *D, |
| const ParsedAttr &AL) { |
| SmallVector<Expr*, 2> Args; |
| if (!checkTryLockFunAttrCommon(S, D, AL, Args)) |
| return; |
| |
| D->addAttr(::new (S.Context) ExclusiveTrylockFunctionAttr( |
| S.Context, AL, AL.getArgAsExpr(0), Args.data(), Args.size())); |
| } |
| |
| static void handleLockReturnedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| // check that the argument is lockable object |
| SmallVector<Expr*, 1> Args; |
| checkAttrArgsAreCapabilityObjs(S, D, AL, Args); |
| unsigned Size = Args.size(); |
| if (Size == 0) |
| return; |
| |
| D->addAttr(::new (S.Context) LockReturnedAttr(S.Context, AL, Args[0])); |
| } |
| |
| static void handleLocksExcludedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (!AL.checkAtLeastNumArgs(S, 1)) |
| return; |
| |
| // check that all arguments are lockable objects |
| SmallVector<Expr*, 1> Args; |
| checkAttrArgsAreCapabilityObjs(S, D, AL, Args); |
| unsigned Size = Args.size(); |
| if (Size == 0) |
| return; |
| Expr **StartArg = &Args[0]; |
| |
| D->addAttr(::new (S.Context) |
| LocksExcludedAttr(S.Context, AL, StartArg, Size)); |
| } |
| |
| static bool checkFunctionConditionAttr(Sema &S, Decl *D, const ParsedAttr &AL, |
| Expr *&Cond, StringRef &Msg) { |
| Cond = AL.getArgAsExpr(0); |
| if (!Cond->isTypeDependent()) { |
| ExprResult Converted = S.PerformContextuallyConvertToBool(Cond); |
| if (Converted.isInvalid()) |
| return false; |
| Cond = Converted.get(); |
| } |
| |
| if (!S.checkStringLiteralArgumentAttr(AL, 1, Msg)) |
| return false; |
| |
| if (Msg.empty()) |
| Msg = "<no message provided>"; |
| |
| SmallVector<PartialDiagnosticAt, 8> Diags; |
| if (isa<FunctionDecl>(D) && !Cond->isValueDependent() && |
| !Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(D), |
| Diags)) { |
| S.Diag(AL.getLoc(), diag::err_attr_cond_never_constant_expr) << AL; |
| for (const PartialDiagnosticAt &PDiag : Diags) |
| S.Diag(PDiag.first, PDiag.second); |
| return false; |
| } |
| return true; |
| } |
| |
| static void handleEnableIfAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| S.Diag(AL.getLoc(), diag::ext_clang_enable_if); |
| |
| Expr *Cond; |
| StringRef Msg; |
| if (checkFunctionConditionAttr(S, D, AL, Cond, Msg)) |
| D->addAttr(::new (S.Context) EnableIfAttr(S.Context, AL, Cond, Msg)); |
| } |
| |
| static void handleErrorAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| StringRef NewUserDiagnostic; |
| if (!S.checkStringLiteralArgumentAttr(AL, 0, NewUserDiagnostic)) |
| return; |
| if (ErrorAttr *EA = S.mergeErrorAttr(D, AL, NewUserDiagnostic)) |
| D->addAttr(EA); |
| } |
| |
| namespace { |
| /// Determines if a given Expr references any of the given function's |
| /// ParmVarDecls, or the function's implicit `this` parameter (if applicable). |
| class ArgumentDependenceChecker |
| : public RecursiveASTVisitor<ArgumentDependenceChecker> { |
| #ifndef NDEBUG |
| const CXXRecordDecl *ClassType; |
| #endif |
| llvm::SmallPtrSet<const ParmVarDecl *, 16> Parms; |
| bool Result; |
| |
| public: |
| ArgumentDependenceChecker(const FunctionDecl *FD) { |
| #ifndef NDEBUG |
| if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) |
| ClassType = MD->getParent(); |
| else |
| ClassType = nullptr; |
| #endif |
| Parms.insert(FD->param_begin(), FD->param_end()); |
| } |
| |
| bool referencesArgs(Expr *E) { |
| Result = false; |
| TraverseStmt(E); |
| return Result; |
| } |
| |
| bool VisitCXXThisExpr(CXXThisExpr *E) { |
| assert(E->getType()->getPointeeCXXRecordDecl() == ClassType && |
| "`this` doesn't refer to the enclosing class?"); |
| Result = true; |
| return false; |
| } |
| |
| bool VisitDeclRefExpr(DeclRefExpr *DRE) { |
| if (const auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) |
| if (Parms.count(PVD)) { |
| Result = true; |
| return false; |
| } |
| return true; |
| } |
| }; |
| } |
| |
| static void handleDiagnoseIfAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| S.Diag(AL.getLoc(), diag::ext_clang_diagnose_if); |
| |
| Expr *Cond; |
| StringRef Msg; |
| if (!checkFunctionConditionAttr(S, D, AL, Cond, Msg)) |
| return; |
| |
| StringRef DiagTypeStr; |
| if (!S.checkStringLiteralArgumentAttr(AL, 2, DiagTypeStr)) |
| return; |
| |
| DiagnoseIfAttr::DiagnosticType DiagType; |
| if (!DiagnoseIfAttr::ConvertStrToDiagnosticType(DiagTypeStr, DiagType)) { |
| S.Diag(AL.getArgAsExpr(2)->getBeginLoc(), |
| diag::err_diagnose_if_invalid_diagnostic_type); |
| return; |
| } |
| |
| bool ArgDependent = false; |
| if (const auto *FD = dyn_cast<FunctionDecl>(D)) |
| ArgDependent = ArgumentDependenceChecker(FD).referencesArgs(Cond); |
| D->addAttr(::new (S.Context) DiagnoseIfAttr( |
| S.Context, AL, Cond, Msg, DiagType, ArgDependent, cast<NamedDecl>(D))); |
| } |
| |
| static void handleNoBuiltinAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| static constexpr const StringRef kWildcard = "*"; |
| |
| llvm::SmallVector<StringRef, 16> Names; |
| bool HasWildcard = false; |
| |
| const auto AddBuiltinName = [&Names, &HasWildcard](StringRef Name) { |
| if (Name == kWildcard) |
| HasWildcard = true; |
| Names.push_back(Name); |
| }; |
| |
| // Add previously defined attributes. |
| if (const auto *NBA = D->getAttr<NoBuiltinAttr>()) |
| for (StringRef BuiltinName : NBA->builtinNames()) |
| AddBuiltinName(BuiltinName); |
| |
| // Add current attributes. |
| if (AL.getNumArgs() == 0) |
| AddBuiltinName(kWildcard); |
| else |
| for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) { |
| StringRef BuiltinName; |
| SourceLocation LiteralLoc; |
| if (!S.checkStringLiteralArgumentAttr(AL, I, BuiltinName, &LiteralLoc)) |
| return; |
| |
| if (Builtin::Context::isBuiltinFunc(BuiltinName)) |
| AddBuiltinName(BuiltinName); |
| else |
| S.Diag(LiteralLoc, diag::warn_attribute_no_builtin_invalid_builtin_name) |
| << BuiltinName << AL; |
| } |
| |
| // Repeating the same attribute is fine. |
| llvm::sort(Names); |
| Names.erase(std::unique(Names.begin(), Names.end()), Names.end()); |
| |
| // Empty no_builtin must be on its own. |
| if (HasWildcard && Names.size() > 1) |
| S.Diag(D->getLocation(), |
| diag::err_attribute_no_builtin_wildcard_or_builtin_name) |
| << AL; |
| |
| if (D->hasAttr<NoBuiltinAttr>()) |
| D->dropAttr<NoBuiltinAttr>(); |
| D->addAttr(::new (S.Context) |
| NoBuiltinAttr(S.Context, AL, Names.data(), Names.size())); |
| } |
| |
| static void handlePassObjectSizeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (D->hasAttr<PassObjectSizeAttr>()) { |
| S.Diag(D->getBeginLoc(), diag::err_attribute_only_once_per_parameter) << AL; |
| return; |
| } |
| |
| Expr *E = AL.getArgAsExpr(0); |
| uint32_t Type; |
| if (!checkUInt32Argument(S, AL, E, Type, /*Idx=*/1)) |
| return; |
| |
| // pass_object_size's argument is passed in as the second argument of |
| // __builtin_object_size. So, it has the same constraints as that second |
| // argument; namely, it must be in the range [0, 3]. |
| if (Type > 3) { |
| S.Diag(E->getBeginLoc(), diag::err_attribute_argument_out_of_range) |
| << AL << 0 << 3 << E->getSourceRange(); |
| return; |
| } |
| |
| // pass_object_size is only supported on constant pointer parameters; as a |
| // kindness to users, we allow the parameter to be non-const for declarations. |
| // At this point, we have no clue if `D` belongs to a function declaration or |
| // definition, so we defer the constness check until later. |
| if (!cast<ParmVarDecl>(D)->getType()->isPointerType()) { |
| S.Diag(D->getBeginLoc(), diag::err_attribute_pointers_only) << AL << 1; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) PassObjectSizeAttr(S.Context, AL, (int)Type)); |
| } |
| |
| static void handleConsumableAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| ConsumableAttr::ConsumedState DefaultState; |
| |
| if (AL.isArgIdent(0)) { |
| IdentifierLoc *IL = AL.getArgAsIdent(0); |
| if (!ConsumableAttr::ConvertStrToConsumedState(IL->Ident->getName(), |
| DefaultState)) { |
| S.Diag(IL->Loc, diag::warn_attribute_type_not_supported) << AL |
| << IL->Ident; |
| return; |
| } |
| } else { |
| S.Diag(AL.getLoc(), diag::err_attribute_argument_type) |
| << AL << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) ConsumableAttr(S.Context, AL, DefaultState)); |
| } |
| |
| static bool checkForConsumableClass(Sema &S, const CXXMethodDecl *MD, |
| const ParsedAttr &AL) { |
| QualType ThisType = MD->getThisType()->getPointeeType(); |
| |
| if (const CXXRecordDecl *RD = ThisType->getAsCXXRecordDecl()) { |
| if (!RD->hasAttr<ConsumableAttr>()) { |
| S.Diag(AL.getLoc(), diag::warn_attr_on_unconsumable_class) << RD; |
| |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| static void handleCallableWhenAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (!AL.checkAtLeastNumArgs(S, 1)) |
| return; |
| |
| if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), AL)) |
| return; |
| |
| SmallVector<CallableWhenAttr::ConsumedState, 3> States; |
| for (unsigned ArgIndex = 0; ArgIndex < AL.getNumArgs(); ++ArgIndex) { |
| CallableWhenAttr::ConsumedState CallableState; |
| |
| StringRef StateString; |
| SourceLocation Loc; |
| if (AL.isArgIdent(ArgIndex)) { |
| IdentifierLoc *Ident = AL.getArgAsIdent(ArgIndex); |
| StateString = Ident->Ident->getName(); |
| Loc = Ident->Loc; |
| } else { |
| if (!S.checkStringLiteralArgumentAttr(AL, ArgIndex, StateString, &Loc)) |
| return; |
| } |
| |
| if (!CallableWhenAttr::ConvertStrToConsumedState(StateString, |
| CallableState)) { |
| S.Diag(Loc, diag::warn_attribute_type_not_supported) << AL << StateString; |
| return; |
| } |
| |
| States.push_back(CallableState); |
| } |
| |
| D->addAttr(::new (S.Context) |
| CallableWhenAttr(S.Context, AL, States.data(), States.size())); |
| } |
| |
| static void handleParamTypestateAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| ParamTypestateAttr::ConsumedState ParamState; |
| |
| if (AL.isArgIdent(0)) { |
| IdentifierLoc *Ident = AL.getArgAsIdent(0); |
| StringRef StateString = Ident->Ident->getName(); |
| |
| if (!ParamTypestateAttr::ConvertStrToConsumedState(StateString, |
| ParamState)) { |
| S.Diag(Ident->Loc, diag::warn_attribute_type_not_supported) |
| << AL << StateString; |
| return; |
| } |
| } else { |
| S.Diag(AL.getLoc(), diag::err_attribute_argument_type) |
| << AL << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| // FIXME: This check is currently being done in the analysis. It can be |
| // enabled here only after the parser propagates attributes at |
| // template specialization definition, not declaration. |
| //QualType ReturnType = cast<ParmVarDecl>(D)->getType(); |
| //const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl(); |
| // |
| //if (!RD || !RD->hasAttr<ConsumableAttr>()) { |
| // S.Diag(AL.getLoc(), diag::warn_return_state_for_unconsumable_type) << |
| // ReturnType.getAsString(); |
| // return; |
| //} |
| |
| D->addAttr(::new (S.Context) ParamTypestateAttr(S.Context, AL, ParamState)); |
| } |
| |
| static void handleReturnTypestateAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| ReturnTypestateAttr::ConsumedState ReturnState; |
| |
| if (AL.isArgIdent(0)) { |
| IdentifierLoc *IL = AL.getArgAsIdent(0); |
| if (!ReturnTypestateAttr::ConvertStrToConsumedState(IL->Ident->getName(), |
| ReturnState)) { |
| S.Diag(IL->Loc, diag::warn_attribute_type_not_supported) << AL |
| << IL->Ident; |
| return; |
| } |
| } else { |
| S.Diag(AL.getLoc(), diag::err_attribute_argument_type) |
| << AL << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| // FIXME: This check is currently being done in the analysis. It can be |
| // enabled here only after the parser propagates attributes at |
| // template specialization definition, not declaration. |
| //QualType ReturnType; |
| // |
| //if (const ParmVarDecl *Param = dyn_cast<ParmVarDecl>(D)) { |
| // ReturnType = Param->getType(); |
| // |
| //} else if (const CXXConstructorDecl *Constructor = |
| // dyn_cast<CXXConstructorDecl>(D)) { |
| // ReturnType = Constructor->getThisType()->getPointeeType(); |
| // |
| //} else { |
| // |
| // ReturnType = cast<FunctionDecl>(D)->getCallResultType(); |
| //} |
| // |
| //const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl(); |
| // |
| //if (!RD || !RD->hasAttr<ConsumableAttr>()) { |
| // S.Diag(Attr.getLoc(), diag::warn_return_state_for_unconsumable_type) << |
| // ReturnType.getAsString(); |
| // return; |
| //} |
| |
| D->addAttr(::new (S.Context) ReturnTypestateAttr(S.Context, AL, ReturnState)); |
| } |
| |
| static void handleSetTypestateAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), AL)) |
| return; |
| |
| SetTypestateAttr::ConsumedState NewState; |
| if (AL.isArgIdent(0)) { |
| IdentifierLoc *Ident = AL.getArgAsIdent(0); |
| StringRef Param = Ident->Ident->getName(); |
| if (!SetTypestateAttr::ConvertStrToConsumedState(Param, NewState)) { |
| S.Diag(Ident->Loc, diag::warn_attribute_type_not_supported) << AL |
| << Param; |
| return; |
| } |
| } else { |
| S.Diag(AL.getLoc(), diag::err_attribute_argument_type) |
| << AL << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) SetTypestateAttr(S.Context, AL, NewState)); |
| } |
| |
| static void handleTestTypestateAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), AL)) |
| return; |
| |
| TestTypestateAttr::ConsumedState TestState; |
| if (AL.isArgIdent(0)) { |
| IdentifierLoc *Ident = AL.getArgAsIdent(0); |
| StringRef Param = Ident->Ident->getName(); |
| if (!TestTypestateAttr::ConvertStrToConsumedState(Param, TestState)) { |
| S.Diag(Ident->Loc, diag::warn_attribute_type_not_supported) << AL |
| << Param; |
| return; |
| } |
| } else { |
| S.Diag(AL.getLoc(), diag::err_attribute_argument_type) |
| << AL << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) TestTypestateAttr(S.Context, AL, TestState)); |
| } |
| |
| static void handleExtVectorTypeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| // Remember this typedef decl, we will need it later for diagnostics. |
| S.ExtVectorDecls.push_back(cast<TypedefNameDecl>(D)); |
| } |
| |
| static void handlePackedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (auto *TD = dyn_cast<TagDecl>(D)) |
| TD->addAttr(::new (S.Context) PackedAttr(S.Context, AL)); |
| else if (auto *FD = dyn_cast<FieldDecl>(D)) { |
| bool BitfieldByteAligned = (!FD->getType()->isDependentType() && |
| !FD->getType()->isIncompleteType() && |
| FD->isBitField() && |
| S.Context.getTypeAlign(FD->getType()) <= 8); |
| |
| if (S.getASTContext().getTargetInfo().getTriple().isPS4()) { |
| if (BitfieldByteAligned) |
| // The PS4 target needs to maintain ABI backwards compatibility. |
| S.Diag(AL.getLoc(), diag::warn_attribute_ignored_for_field_of_type) |
| << AL << FD->getType(); |
| else |
| FD->addAttr(::new (S.Context) PackedAttr(S.Context, AL)); |
| } else { |
| // Report warning about changed offset in the newer compiler versions. |
| if (BitfieldByteAligned) |
| S.Diag(AL.getLoc(), diag::warn_attribute_packed_for_bitfield); |
| |
| FD->addAttr(::new (S.Context) PackedAttr(S.Context, AL)); |
| } |
| |
| } else |
| S.Diag(AL.getLoc(), diag::warn_attribute_ignored) << AL; |
| } |
| |
| static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) { |
| auto *RD = cast<CXXRecordDecl>(D); |
| ClassTemplateDecl *CTD = RD->getDescribedClassTemplate(); |
| assert(CTD && "attribute does not appertain to this declaration"); |
| |
| ParsedType PT = AL.getTypeArg(); |
| TypeSourceInfo *TSI = nullptr; |
| QualType T = S.GetTypeFromParser(PT, &TSI); |
| if (!TSI) |
| TSI = S.Context.getTrivialTypeSourceInfo(T, AL.getLoc()); |
| |
| if (!T.hasQualifiers() && T->isTypedefNameType()) { |
| // Find the template name, if this type names a template specialization. |
| const TemplateDecl *Template = nullptr; |
| if (const auto *CTSD = dyn_cast_or_null<ClassTemplateSpecializationDecl>( |
| T->getAsCXXRecordDecl())) { |
| Template = CTSD->getSpecializedTemplate(); |
| } else if (const auto *TST = T->getAs<TemplateSpecializationType>()) { |
| while (TST && TST->isTypeAlias()) |
| TST = TST->getAliasedType()->getAs<TemplateSpecializationType>(); |
| if (TST) |
| Template = TST->getTemplateName().getAsTemplateDecl(); |
| } |
| |
| if (Template && declaresSameEntity(Template, CTD)) { |
| D->addAttr(::new (S.Context) PreferredNameAttr(S.Context, AL, TSI)); |
| return; |
| } |
| } |
| |
| S.Diag(AL.getLoc(), diag::err_attribute_preferred_name_arg_invalid) |
| << T << CTD; |
| if (const auto *TT = T->getAs<TypedefType>()) |
| S.Diag(TT->getDecl()->getLocation(), diag::note_entity_declared_at) |
| << TT->getDecl(); |
| } |
| |
| static bool checkIBOutletCommon(Sema &S, Decl *D, const ParsedAttr &AL) { |
| // The IBOutlet/IBOutletCollection attributes only apply to instance |
| // variables or properties of Objective-C classes. The outlet must also |
| // have an object reference type. |
| if (const auto *VD = dyn_cast<ObjCIvarDecl>(D)) { |
| if (!VD->getType()->getAs<ObjCObjectPointerType>()) { |
| S.Diag(AL.getLoc(), diag::warn_iboutlet_object_type) |
| << AL << VD->getType() << 0; |
| return false; |
| } |
| } |
| else if (const auto *PD = dyn_cast<ObjCPropertyDecl>(D)) { |
| if (!PD->getType()->getAs<ObjCObjectPointerType>()) { |
| S.Diag(AL.getLoc(), diag::warn_iboutlet_object_type) |
| << AL << PD->getType() << 1; |
| return false; |
| } |
| } |
| else { |
| S.Diag(AL.getLoc(), diag::warn_attribute_iboutlet) << AL; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static void handleIBOutlet(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (!checkIBOutletCommon(S, D, AL)) |
| return; |
| |
| D->addAttr(::new (S.Context) IBOutletAttr(S.Context, AL)); |
| } |
| |
| static void handleIBOutletCollection(Sema &S, Decl *D, const ParsedAttr &AL) { |
| |
| // The iboutletcollection attribute can have zero or one arguments. |
| if (AL.getNumArgs() > 1) { |
| S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1; |
| return; |
| } |
| |
| if (!checkIBOutletCommon(S, D, AL)) |
| return; |
| |
| ParsedType PT; |
| |
| if (AL.hasParsedType()) |
| PT = AL.getTypeArg(); |
| else { |
| PT = S.getTypeName(S.Context.Idents.get("NSObject"), AL.getLoc(), |
| S.getScopeForContext(D->getDeclContext()->getParent())); |
| if (!PT) { |
| S.Diag(AL.getLoc(), diag::err_iboutletcollection_type) << "NSObject"; |
| return; |
| } |
| } |
| |
| TypeSourceInfo *QTLoc = nullptr; |
| QualType QT = S.GetTypeFromParser(PT, &QTLoc); |
| if (!QTLoc) |
| QTLoc = S.Context.getTrivialTypeSourceInfo(QT, AL.getLoc()); |
| |
| // Diagnose use of non-object type in iboutletcollection attribute. |
| // FIXME. Gnu attribute extension ignores use of builtin types in |
| // attributes. So, __attribute__((iboutletcollection(char))) will be |
| // treated as __attribute__((iboutletcollection())). |
| if (!QT->isObjCIdType() && !QT->isObjCObjectType()) { |
| S.Diag(AL.getLoc(), |
| QT->isBuiltinType() ? diag::err_iboutletcollection_builtintype |
| : diag::err_iboutletcollection_type) << QT; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) IBOutletCollectionAttr(S.Context, AL, QTLoc)); |
| } |
| |
| bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) { |
| if (RefOkay) { |
| if (T->isReferenceType()) |
| return true; |
| } else { |
| T = T.getNonReferenceType(); |
| } |
| |
| // The nonnull attribute, and other similar attributes, can be applied to a |
| // transparent union that contains a pointer type. |
| if (const RecordType *UT = T->getAsUnionType()) { |
| if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>()) { |
| RecordDecl *UD = UT->getDecl(); |
| for (const auto *I : UD->fields()) { |
| QualType QT = I->getType(); |
| if (QT->isAnyPointerType() || QT->isBlockPointerType()) |
| return true; |
| } |
| } |
| } |
| |
| return T->isAnyPointerType() || T->isBlockPointerType(); |
| } |
| |
| static bool attrNonNullArgCheck(Sema &S, QualType T, const ParsedAttr &AL, |
| SourceRange AttrParmRange, |
| SourceRange TypeRange, |
| bool isReturnValue = false) { |
| if (!S.isValidPointerAttrType(T)) { |
| if (isReturnValue) |
| S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only) |
| << AL << AttrParmRange << TypeRange; |
| else |
| S.Diag(AL.getLoc(), diag::warn_attribute_pointers_only) |
| << AL << AttrParmRange << TypeRange << 0; |
| return false; |
| } |
| return true; |
| } |
| |
| static void handleNonNullAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| SmallVector<ParamIdx, 8> NonNullArgs; |
| for (unsigned I = 0; I < AL.getNumArgs(); ++I) { |
| Expr *Ex = AL.getArgAsExpr(I); |
| ParamIdx Idx; |
| if (!checkFunctionOrMethodParameterIndex(S, D, AL, I + 1, Ex, Idx)) |
| return; |
| |
| // Is the function argument a pointer type? |
| if (Idx.getASTIndex() < getFunctionOrMethodNumParams(D) && |
| !attrNonNullArgCheck( |
| S, getFunctionOrMethodParamType(D, Idx.getASTIndex()), AL, |
| Ex->getSourceRange(), |
| getFunctionOrMethodParamRange(D, Idx.getASTIndex()))) |
| continue; |
| |
| NonNullArgs.push_back(Idx); |
| } |
| |
| // If no arguments were specified to __attribute__((nonnull)) then all pointer |
| // arguments have a nonnull attribute; warn if there aren't any. Skip this |
| // check if the attribute came from a macro expansion or a template |
| // instantiation. |
| if (NonNullArgs.empty() && AL.getLoc().isFileID() && |
| !S.inTemplateInstantiation()) { |
| bool AnyPointers = isFunctionOrMethodVariadic(D); |
| for (unsigned I = 0, E = getFunctionOrMethodNumParams(D); |
| I != E && !AnyPointers; ++I) { |
| QualType T = getFunctionOrMethodParamType(D, I); |
| if (T->isDependentType() || S.isValidPointerAttrType(T)) |
| AnyPointers = true; |
| } |
| |
| if (!AnyPointers) |
| S.Diag(AL.getLoc(), diag::warn_attribute_nonnull_no_pointers); |
| } |
| |
| ParamIdx *Start = NonNullArgs.data(); |
| unsigned Size = NonNullArgs.size(); |
| llvm::array_pod_sort(Start, Start + Size); |
| D->addAttr(::new (S.Context) NonNullAttr(S.Context, AL, Start, Size)); |
| } |
| |
| static void handleNonNullAttrParameter(Sema &S, ParmVarDecl *D, |
| const ParsedAttr &AL) { |
| if (AL.getNumArgs() > 0) { |
| if (D->getFunctionType()) { |
| handleNonNullAttr(S, D, AL); |
| } else { |
| S.Diag(AL.getLoc(), diag::warn_attribute_nonnull_parm_no_args) |
| << D->getSourceRange(); |
| } |
| return; |
| } |
| |
| // Is the argument a pointer type? |
| if (!attrNonNullArgCheck(S, D->getType(), AL, SourceRange(), |
| D->getSourceRange())) |
| return; |
| |
| D->addAttr(::new (S.Context) NonNullAttr(S.Context, AL, nullptr, 0)); |
| } |
| |
| static void handleReturnsNonNullAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| QualType ResultType = getFunctionOrMethodResultType(D); |
| SourceRange SR = getFunctionOrMethodResultSourceRange(D); |
| if (!attrNonNullArgCheck(S, ResultType, AL, SourceRange(), SR, |
| /* isReturnValue */ true)) |
| return; |
| |
| D->addAttr(::new (S.Context) ReturnsNonNullAttr(S.Context, AL)); |
| } |
| |
| static void handleNoEscapeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (D->isInvalidDecl()) |
| return; |
| |
| // noescape only applies to pointer types. |
| QualType T = cast<ParmVarDecl>(D)->getType(); |
| if (!S.isValidPointerAttrType(T, /* RefOkay */ true)) { |
| S.Diag(AL.getLoc(), diag::warn_attribute_pointers_only) |
| << AL << AL.getRange() << 0; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) NoEscapeAttr(S.Context, AL)); |
| } |
| |
| static void handleAssumeAlignedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| Expr *E = AL.getArgAsExpr(0), |
| *OE = AL.getNumArgs() > 1 ? AL.getArgAsExpr(1) : nullptr; |
| S.AddAssumeAlignedAttr(D, AL, E, OE); |
| } |
| |
| static void handleAllocAlignAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| S.AddAllocAlignAttr(D, AL, AL.getArgAsExpr(0)); |
| } |
| |
| void Sema::AddAssumeAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E, |
| Expr *OE) { |
| QualType ResultType = getFunctionOrMethodResultType(D); |
| SourceRange SR = getFunctionOrMethodResultSourceRange(D); |
| |
| AssumeAlignedAttr TmpAttr(Context, CI, E, OE); |
| SourceLocation AttrLoc = TmpAttr.getLocation(); |
| |
| if (!isValidPointerAttrType(ResultType, /* RefOkay */ true)) { |
| Diag(AttrLoc, diag::warn_attribute_return_pointers_refs_only) |
| << &TmpAttr << TmpAttr.getRange() << SR; |
| return; |
| } |
| |
| if (!E->isValueDependent()) { |
| Optional<llvm::APSInt> I = llvm::APSInt(64); |
| if (!(I = E->getIntegerConstantExpr(Context))) { |
| if (OE) |
| Diag(AttrLoc, diag::err_attribute_argument_n_type) |
| << &TmpAttr << 1 << AANT_ArgumentIntegerConstant |
| << E->getSourceRange(); |
| else |
| Diag(AttrLoc, diag::err_attribute_argument_type) |
| << &TmpAttr << AANT_ArgumentIntegerConstant |
| << E->getSourceRange(); |
| return; |
| } |
| |
| if (!I->isPowerOf2()) { |
| Diag(AttrLoc, diag::err_alignment_not_power_of_two) |
| << E->getSourceRange(); |
| return; |
| } |
| |
| if (*I > Sema::MaximumAlignment) |
| Diag(CI.getLoc(), diag::warn_assume_aligned_too_great) |
| << CI.getRange() << Sema::MaximumAlignment; |
| } |
| |
| if (OE && !OE->isValueDependent() && !OE->isIntegerConstantExpr(Context)) { |
| Diag(AttrLoc, diag::err_attribute_argument_n_type) |
| << &TmpAttr << 2 << AANT_ArgumentIntegerConstant |
| << OE->getSourceRange(); |
| return; |
| } |
| |
| D->addAttr(::new (Context) AssumeAlignedAttr(Context, CI, E, OE)); |
| } |
| |
| void Sema::AddAllocAlignAttr(Decl *D, const AttributeCommonInfo &CI, |
| Expr *ParamExpr) { |
| QualType ResultType = getFunctionOrMethodResultType(D); |
| |
| AllocAlignAttr TmpAttr(Context, CI, ParamIdx()); |
| SourceLocation AttrLoc = CI.getLoc(); |
| |
| if (!ResultType->isDependentType() && |
| !isValidPointerAttrType(ResultType, /* RefOkay */ true)) { |
| Diag(AttrLoc, diag::warn_attribute_return_pointers_refs_only) |
| << &TmpAttr << CI.getRange() << getFunctionOrMethodResultSourceRange(D); |
| return; |
| } |
| |
| ParamIdx Idx; |
| const auto *FuncDecl = cast<FunctionDecl>(D); |
| if (!checkFunctionOrMethodParameterIndex(*this, FuncDecl, TmpAttr, |
| /*AttrArgNum=*/1, ParamExpr, Idx)) |
| return; |
| |
| QualType Ty = getFunctionOrMethodParamType(D, Idx.getASTIndex()); |
| if (!Ty->isDependentType() && !Ty->isIntegralType(Context) && |
| !Ty->isAlignValT()) { |
| Diag(ParamExpr->getBeginLoc(), diag::err_attribute_integers_only) |
| << &TmpAttr |
| << FuncDecl->getParamDecl(Idx.getASTIndex())->getSourceRange(); |
| return; |
| } |
| |
| D->addAttr(::new (Context) AllocAlignAttr(Context, CI, Idx)); |
| } |
| |
| /// Check if \p AssumptionStr is a known assumption and warn if not. |
| static void checkAssumptionAttr(Sema &S, SourceLocation Loc, |
| StringRef AssumptionStr) { |
| if (llvm::KnownAssumptionStrings.count(AssumptionStr)) |
| return; |
| |
| unsigned BestEditDistance = 3; |
| StringRef Suggestion; |
| for (const auto &KnownAssumptionIt : llvm::KnownAssumptionStrings) { |
| unsigned EditDistance = |
| AssumptionStr.edit_distance(KnownAssumptionIt.getKey()); |
| if (EditDistance < BestEditDistance) { |
| Suggestion = KnownAssumptionIt.getKey(); |
| BestEditDistance = EditDistance; |
| } |
| } |
| |
| if (!Suggestion.empty()) |
| S.Diag(Loc, diag::warn_assume_attribute_string_unknown_suggested) |
| << AssumptionStr << Suggestion; |
| else |
| S.Diag(Loc, diag::warn_assume_attribute_string_unknown) << AssumptionStr; |
| } |
| |
| static void handleAssumumptionAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| // Handle the case where the attribute has a text message. |
| StringRef Str; |
| SourceLocation AttrStrLoc; |
| if (!S.checkStringLiteralArgumentAttr(AL, 0, Str, &AttrStrLoc)) |
| return; |
| |
| checkAssumptionAttr(S, AttrStrLoc, Str); |
| |
| D->addAttr(::new (S.Context) AssumptionAttr(S.Context, AL, Str)); |
| } |
| |
| /// Normalize the attribute, __foo__ becomes foo. |
| /// Returns true if normalization was applied. |
| static bool normalizeName(StringRef &AttrName) { |
| if (AttrName.size() > 4 && AttrName.startswith("__") && |
| AttrName.endswith("__")) { |
| AttrName = AttrName.drop_front(2).drop_back(2); |
| return true; |
| } |
| return false; |
| } |
| |
| static void handleOwnershipAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| // This attribute must be applied to a function declaration. The first |
| // argument to the attribute must be an identifier, the name of the resource, |
| // for example: malloc. The following arguments must be argument indexes, the |
| // arguments must be of integer type for Returns, otherwise of pointer type. |
| // The difference between Holds and Takes is that a pointer may still be used |
| // after being held. free() should be __attribute((ownership_takes)), whereas |
| // a list append function may well be __attribute((ownership_holds)). |
| |
| if (!AL.isArgIdent(0)) { |
| S.Diag(AL.getLoc(), diag::err_attribute_argument_n_type) |
| << AL << 1 << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| // Figure out our Kind. |
| OwnershipAttr::OwnershipKind K = |
| OwnershipAttr(S.Context, AL, nullptr, nullptr, 0).getOwnKind(); |
| |
| // Check arguments. |
| switch (K) { |
| case OwnershipAttr::Takes: |
| case OwnershipAttr::Holds: |
| if (AL.getNumArgs() < 2) { |
| S.Diag(AL.getLoc(), diag::err_attribute_too_few_arguments) << AL << 2; |
| return; |
| } |
| break; |
| case OwnershipAttr::Returns: |
| if (AL.getNumArgs() > 2) { |
| S.Diag(AL.getLoc(), diag::err_attribute_too_many_arguments) << AL << 1; |
| return; |
| } |
| break; |
| } |
| |
| IdentifierInfo *Module = AL.getArgAsIdent(0)->Ident; |
| |
| StringRef ModuleName = Module->getName(); |
| if (normalizeName(ModuleName)) { |
| Module = &S.PP.getIdentifierTable().get(ModuleName); |
| } |
| |
| SmallVector<ParamIdx, 8> OwnershipArgs; |
| for (unsigned i = 1; i < AL.getNumArgs(); ++i) { |
| Expr *Ex = AL.getArgAsExpr(i); |
| ParamIdx Idx; |
| if (!checkFunctionOrMethodParameterIndex(S, D, AL, i, Ex, Idx)) |
| return; |
| |
| // Is the function argument a pointer type? |
| QualType T = getFunctionOrMethodParamType(D, Idx.getASTIndex()); |
| int Err = -1; // No error |
| switch (K) { |
| case OwnershipAttr::Takes: |
| case OwnershipAttr::Holds: |
| if (!T->isAnyPointerType() && !T->isBlockPointerType()) |
| Err = 0; |
| break; |
| case OwnershipAttr::Returns: |
| if (!T->isIntegerType()) |
| Err = 1; |
| break; |
| } |
| if (-1 != Err) { |
| S.Diag(AL.getLoc(), diag::err_ownership_type) << AL << Err |
| << Ex->getSourceRange(); |
| return; |
| } |
| |
| // Check we don't have a conflict with another ownership attribute. |
| for (const auto *I : D->specific_attrs<OwnershipAttr>()) { |
| // Cannot have two ownership attributes of different kinds for the same |
| // index. |
| if (I->getOwnKind() != K && I->args_end() != |
| std::find(I->args_begin(), I->args_end(), Idx)) { |
| S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible) << AL << I; |
| return; |
| } else if (K == OwnershipAttr::Returns && |
| I->getOwnKind() == OwnershipAttr::Returns) { |
| // A returns attribute conflicts with any other returns attribute using |
| // a different index. |
| if (!llvm::is_contained(I->args(), Idx)) { |
| S.Diag(I->getLocation(), diag::err_ownership_returns_index_mismatch) |
| << I->args_begin()->getSourceIndex(); |
| if (I->args_size()) |
| S.Diag(AL.getLoc(), diag::note_ownership_returns_index_mismatch) |
| << Idx.getSourceIndex() << Ex->getSourceRange(); |
| return; |
| } |
| } |
| } |
| OwnershipArgs.push_back(Idx); |
| } |
| |
| ParamIdx *Start = OwnershipArgs.data(); |
| unsigned Size = OwnershipArgs.size(); |
| llvm::array_pod_sort(Start, Start + Size); |
| D->addAttr(::new (S.Context) |
| OwnershipAttr(S.Context, AL, Module, Start, Size)); |
| } |
| |
| static void handleWeakRefAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| // Check the attribute arguments. |
| if (AL.getNumArgs() > 1) { |
| S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1; |
| return; |
| } |
| |
| // gcc rejects |
| // class c { |
| // static int a __attribute__((weakref ("v2"))); |
| // static int b() __attribute__((weakref ("f3"))); |
| // }; |
| // and ignores the attributes of |
| // void f(void) { |
| // static int a __attribute__((weakref ("v2"))); |
| // } |
| // we reject them |
| const DeclContext *Ctx = D->getDeclContext()->getRedeclContext(); |
| if (!Ctx->isFileContext()) { |
| S.Diag(AL.getLoc(), diag::err_attribute_weakref_not_global_context) |
| << cast<NamedDecl>(D); |
| return; |
| } |
| |
| // The GCC manual says |
| // |
| // At present, a declaration to which `weakref' is attached can only |
| // be `static'. |
| // |
| // It also says |
| // |
| // Without a TARGET, |
| // given as an argument to `weakref' or to `alias', `weakref' is |
| // equivalent to `weak'. |
| // |
| // gcc 4.4.1 will accept |
| // int a7 __attribute__((weakref)); |
| // as |
| // int a7 __attribute__((weak)); |
| // This looks like a bug in gcc. We reject that for now. We should revisit |
| // it if this behaviour is actually used. |
| |
| // GCC rejects |
| // static ((alias ("y"), weakref)). |
| // Should we? How to check that weakref is before or after alias? |
| |
| // FIXME: it would be good for us to keep the WeakRefAttr as-written instead |
| // of transforming it into an AliasAttr. The WeakRefAttr never uses the |
| // StringRef parameter it was given anyway. |
| StringRef Str; |
| if (AL.getNumArgs() && S.checkStringLiteralArgumentAttr(AL, 0, Str)) |
| // GCC will accept anything as the argument of weakref. Should we |
| // check for an existing decl? |
| D->addAttr(::new (S.Context) AliasAttr(S.Context, AL, Str)); |
| |
| D->addAttr(::new (S.Context) WeakRefAttr(S.Context, AL)); |
| } |
| |
| static void handleIFuncAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| StringRef Str; |
| if (!S.checkStringLiteralArgumentAttr(AL, 0, Str)) |
| return; |
| |
| // Aliases should be on declarations, not definitions. |
| const auto *FD = cast<FunctionDecl>(D); |
| if (FD->isThisDeclarationADefinition()) { |
| S.Diag(AL.getLoc(), diag::err_alias_is_definition) << FD << 1; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) IFuncAttr(S.Context, AL, Str)); |
| } |
| |
| static void handleAliasAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| StringRef Str; |
| if (!S.checkStringLiteralArgumentAttr(AL, 0, Str)) |
| return; |
| |
| if (S.Context.getTargetInfo().getTriple().isOSDarwin()) { |
| S.Diag(AL.getLoc(), diag::err_alias_not_supported_on_darwin); |
| return; |
| } |
| if (S.Context.getTargetInfo().getTriple().isNVPTX()) { |
| S.Diag(AL.getLoc(), diag::err_alias_not_supported_on_nvptx); |
| } |
| |
| // Aliases should be on declarations, not definitions. |
| if (const auto *FD = dyn_cast<FunctionDecl>(D)) { |
| if (FD->isThisDeclarationADefinition()) { |
| S.Diag(AL.getLoc(), diag::err_alias_is_definition) << FD << 0; |
| return; |
| } |
| } else { |
| const auto *VD = cast<VarDecl>(D); |
| if (VD->isThisDeclarationADefinition() && VD->isExternallyVisible()) { |
| S.Diag(AL.getLoc(), diag::err_alias_is_definition) << VD << 0; |
| return; |
| } |
| } |
| |
| // Mark target used to prevent unneeded-internal-declaration warnings. |
| if (!S.LangOpts.CPlusPlus) { |
| // FIXME: demangle Str for C++, as the attribute refers to the mangled |
| // linkage name, not the pre-mangled identifier. |
| const DeclarationNameInfo target(&S.Context.Idents.get(Str), AL.getLoc()); |
| LookupResult LR(S, target, Sema::LookupOrdinaryName); |
| if (S.LookupQualifiedName(LR, S.getCurLexicalContext())) |
| for (NamedDecl *ND : LR) |
| ND->markUsed(S.Context); |
| } |
| |
| D->addAttr(::new (S.Context) AliasAttr(S.Context, AL, Str)); |
| } |
| |
| static void handleTLSModelAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| StringRef Model; |
| SourceLocation LiteralLoc; |
| // Check that it is a string. |
| if (!S.checkStringLiteralArgumentAttr(AL, 0, Model, &LiteralLoc)) |
| return; |
| |
| // Check that the value. |
| if (Model != "global-dynamic" && Model != "local-dynamic" |
| && Model != "initial-exec" && Model != "local-exec") { |
| S.Diag(LiteralLoc, diag::err_attr_tlsmodel_arg); |
| return; |
| } |
| |
| if (S.Context.getTargetInfo().getTriple().isOSAIX() && |
| Model != "global-dynamic") { |
| S.Diag(LiteralLoc, diag::err_aix_attr_unsupported_tls_model) << Model; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) TLSModelAttr(S.Context, AL, Model)); |
| } |
| |
| static void handleRestrictAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| QualType ResultType = getFunctionOrMethodResultType(D); |
| if (ResultType->isAnyPointerType() || ResultType->isBlockPointerType()) { |
| D->addAttr(::new (S.Context) RestrictAttr(S.Context, AL)); |
| return; |
| } |
| |
| S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only) |
| << AL << getFunctionOrMethodResultSourceRange(D); |
| } |
| |
| static void handleCPUSpecificAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| // Ensure we don't combine these with themselves, since that causes some |
| // confusing behavior. |
| if (AL.getParsedKind() == ParsedAttr::AT_CPUDispatch) { |
| if (checkAttrMutualExclusion<CPUSpecificAttr>(S, D, AL)) |
| return; |
| |
| if (const auto *Other = D->getAttr<CPUDispatchAttr>()) { |
| S.Diag(AL.getLoc(), diag::err_disallowed_duplicate_attribute) << AL; |
| S.Diag(Other->getLocation(), diag::note_conflicting_attribute); |
| return; |
| } |
| } else if (AL.getParsedKind() == ParsedAttr::AT_CPUSpecific) { |
| if (checkAttrMutualExclusion<CPUDispatchAttr>(S, D, AL)) |
| return; |
| |
| if (const auto *Other = D->getAttr<CPUSpecificAttr>()) { |
| S.Diag(AL.getLoc(), diag::err_disallowed_duplicate_attribute) << AL; |
| S.Diag(Other->getLocation(), diag::note_conflicting_attribute); |
| return; |
| } |
| } |
| |
| FunctionDecl *FD = cast<FunctionDecl>(D); |
| |
| if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { |
| if (MD->getParent()->isLambda()) { |
| S.Diag(AL.getLoc(), diag::err_attribute_dll_lambda) << AL; |
| return; |
| } |
| } |
| |
| if (!AL.checkAtLeastNumArgs(S, 1)) |
| return; |
| |
| SmallVector<IdentifierInfo *, 8> CPUs; |
| for (unsigned ArgNo = 0; ArgNo < getNumAttributeArgs(AL); ++ArgNo) { |
| if (!AL.isArgIdent(ArgNo)) { |
| S.Diag(AL.getLoc(), diag::err_attribute_argument_type) |
| << AL << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| IdentifierLoc *CPUArg = AL.getArgAsIdent(ArgNo); |
| StringRef CPUName = CPUArg->Ident->getName().trim(); |
| |
| if (!S.Context.getTargetInfo().validateCPUSpecificCPUDispatch(CPUName)) { |
| S.Diag(CPUArg->Loc, diag::err_invalid_cpu_specific_dispatch_value) |
| << CPUName << (AL.getKind() == ParsedAttr::AT_CPUDispatch); |
| return; |
| } |
| |
| const TargetInfo &Target = S.Context.getTargetInfo(); |
| if (llvm::any_of(CPUs, [CPUName, &Target](const IdentifierInfo *Cur) { |
| return Target.CPUSpecificManglingCharacter(CPUName) == |
| Target.CPUSpecificManglingCharacter(Cur->getName()); |
| })) { |
| S.Diag(AL.getLoc(), diag::warn_multiversion_duplicate_entries); |
| return; |
| } |
| CPUs.push_back(CPUArg->Ident); |
| } |
| |
| FD->setIsMultiVersion(true); |
| if (AL.getKind() == ParsedAttr::AT_CPUSpecific) |
| D->addAttr(::new (S.Context) |
| CPUSpecificAttr(S.Context, AL, CPUs.data(), CPUs.size())); |
| else |
| D->addAttr(::new (S.Context) |
| CPUDispatchAttr(S.Context, AL, CPUs.data(), CPUs.size())); |
| } |
| |
| static void handleCommonAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (S.LangOpts.CPlusPlus) { |
| S.Diag(AL.getLoc(), diag::err_attribute_not_supported_in_lang) |
| << AL << AttributeLangSupport::Cpp; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) CommonAttr(S.Context, AL)); |
| } |
| |
| static void handleCmseNSEntryAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (S.LangOpts.CPlusPlus && !D->getDeclContext()->isExternCContext()) { |
| S.Diag(AL.getLoc(), diag::err_attribute_not_clinkage) << AL; |
| return; |
| } |
| |
| const auto *FD = cast<FunctionDecl>(D); |
| if (!FD->isExternallyVisible()) { |
| S.Diag(AL.getLoc(), diag::warn_attribute_cmse_entry_static); |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) CmseNSEntryAttr(S.Context, AL)); |
| } |
| |
| static void handleNakedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (AL.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(AL.getLoc(), diag::err_attribute_not_supported_on_arch) |
| << AL << Triple.getArchName(); |
| return; |
| } |
| } |
| |
| D->addAttr(::new (S.Context) NakedAttr(S.Context, AL)); |
| } |
| |
| static void handleNoReturnAttr(Sema &S, Decl *D, const ParsedAttr &Attrs) { |
| if (hasDeclarator(D)) return; |
| |
| if (!isa<ObjCMethodDecl>(D)) { |
| S.Diag(Attrs.getLoc(), diag::warn_attribute_wrong_decl_type) |
| << Attrs << ExpectedFunctionOrMethod; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) NoReturnAttr(S.Context, Attrs)); |
| } |
| |
| static void handleNoCfCheckAttr(Sema &S, Decl *D, const ParsedAttr &Attrs) { |
| if (!S.getLangOpts().CFProtectionBranch) |
| S.Diag(Attrs.getLoc(), diag::warn_nocf_check_attribute_ignored); |
| else |
| handleSimpleAttribute<AnyX86NoCfCheckAttr>(S, D, Attrs); |
| } |
| |
| bool Sema::CheckAttrNoArgs(const ParsedAttr &Attrs) { |
| if (!Attrs.checkExactlyNumArgs(*this, 0)) { |
| Attrs.setInvalid(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool Sema::CheckAttrTarget(const ParsedAttr &AL) { |
| // Check whether the attribute is valid on the current target. |
| if (!AL.existsInTarget(Context.getTargetInfo())) { |
| Diag(AL.getLoc(), diag::warn_unknown_attribute_ignored) |
| << AL << AL.getRange(); |
| AL.setInvalid(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static void handleAnalyzerNoReturnAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| |
| // The checking path for 'noreturn' and 'analyzer_noreturn' are different |
| // because 'analyzer_noreturn' does not impact the type. |
| if (!isFunctionOrMethodOrBlock(D)) { |
| ValueDecl *VD = dyn_cast<ValueDecl>(D); |
| if (!VD || (!VD->getType()->isBlockPointerType() && |
| !VD->getType()->isFunctionPointerType())) { |
| S.Diag(AL.getLoc(), AL.isStandardAttributeSyntax() |
| ? diag::err_attribute_wrong_decl_type |
| : diag::warn_attribute_wrong_decl_type) |
| << AL << ExpectedFunctionMethodOrBlock; |
| return; |
| } |
| } |
| |
| D->addAttr(::new (S.Context) AnalyzerNoReturnAttr(S.Context, AL)); |
| } |
| |
| // PS3 PPU-specific. |
| static void handleVecReturnAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| /* |
| Returning a Vector Class in Registers |
| |
| According to the PPU ABI specifications, a class with a single member of |
| vector type is returned in memory when used as the return value of a |
| function. |
| This results in inefficient code when implementing vector classes. To return |
| the value in a single vector register, add the vecreturn attribute to the |
| class definition. This attribute is also applicable to struct types. |
| |
| Example: |
| |
| struct Vector |
| { |
| __vector float xyzw; |
| } __attribute__((vecreturn)); |
| |
| Vector Add(Vector lhs, Vector rhs) |
| { |
| Vector result; |
| result.xyzw = vec_add(lhs.xyzw, rhs.xyzw); |
| return result; // This will be returned in a register |
| } |
| */ |
| if (VecReturnAttr *A = D->getAttr<VecReturnAttr>()) { |
| S.Diag(AL.getLoc(), diag::err_repeat_attribute) << A; |
| return; |
| } |
| |
| const auto *R = cast<RecordDecl>(D); |
| int count = 0; |
| |
| if (!isa<CXXRecordDecl>(R)) { |
| S.Diag(AL.getLoc(), diag::err_attribute_vecreturn_only_vector_member); |
| return; |
| } |
| |
| if (!cast<CXXRecordDecl>(R)->isPOD()) { |
| S.Diag(AL.getLoc(), diag::err_attribute_vecreturn_only_pod_record); |
| return; |
| } |
| |
| for (const auto *I : R->fields()) { |
| if ((count == 1) || !I->getType()->isVectorType()) { |
| S.Diag(AL.getLoc(), diag::err_attribute_vecreturn_only_vector_member); |
| return; |
| } |
| count++; |
| } |
| |
| D->addAttr(::new (S.Context) VecReturnAttr(S.Context, AL)); |
| } |
| |
| static void handleDependencyAttr(Sema &S, Scope *Scope, Decl *D, |
| const ParsedAttr &AL) { |
| if (isa<ParmVarDecl>(D)) { |
| // [[carries_dependency]] can only be applied to a parameter if it is a |
| // parameter of a function declaration or lambda. |
| if (!(Scope->getFlags() & clang::Scope::FunctionDeclarationScope)) { |
| S.Diag(AL.getLoc(), |
| diag::err_carries_dependency_param_not_function_decl); |
| return; |
| } |
| } |
| |
| D->addAttr(::new (S.Context) CarriesDependencyAttr(S.Context, AL)); |
| } |
| |
| static void handleUnusedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| bool IsCXX17Attr = AL.isCXX11Attribute() && !AL.getScopeName(); |
| |
| // If this is spelled as the standard C++17 attribute, but not in C++17, warn |
| // about using it as an extension. |
| if (!S.getLangOpts().CPlusPlus17 && IsCXX17Attr) |
| S.Diag(AL.getLoc(), diag::ext_cxx17_attr) << AL; |
| |
| D->addAttr(::new (S.Context) UnusedAttr(S.Context, AL)); |
| } |
| |
| static void handleConstructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| uint32_t priority = ConstructorAttr::DefaultPriority; |
| if (AL.getNumArgs() && |
| !checkUInt32Argument(S, AL, AL.getArgAsExpr(0), priority)) |
| return; |
| |
| D->addAttr(::new (S.Context) ConstructorAttr(S.Context, AL, priority)); |
| } |
| |
| static void handleDestructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| uint32_t priority = DestructorAttr::DefaultPriority; |
| if (AL.getNumArgs() && |
| !checkUInt32Argument(S, AL, AL.getArgAsExpr(0), priority)) |
| return; |
| |
| D->addAttr(::new (S.Context) DestructorAttr(S.Context, AL, priority)); |
| } |
| |
| template <typename AttrTy> |
| static void handleAttrWithMessage(Sema &S, Decl *D, const ParsedAttr &AL) { |
| // Handle the case where the attribute has a text message. |
| StringRef Str; |
| if (AL.getNumArgs() == 1 && !S.checkStringLiteralArgumentAttr(AL, 0, Str)) |
| return; |
| |
| D->addAttr(::new (S.Context) AttrTy(S.Context, AL, Str)); |
| } |
| |
| static void handleObjCSuppresProtocolAttr(Sema &S, Decl *D, |
| const ParsedAttr &AL) { |
| if (!cast<ObjCProtocolDecl>(D)->isThisDeclarationADefinition()) { |
| S.Diag(AL.getLoc(), diag::err_objc_attr_protocol_requires_definition) |
| << AL << AL.getRange(); |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) ObjCExplicitProtocolImplAttr(S.Context, AL)); |
| } |
| |
| static bool checkAvailabilityAttr(Sema &S, SourceRange Range, |
| IdentifierInfo *Platform, |
| VersionTuple Introduced, |
| VersionTuple Deprecated, |
| VersionTuple Obsoleted) { |
| StringRef PlatformName |
| = AvailabilityAttr::getPrettyPlatformName(Platform->getName()); |
| if (PlatformName.empty()) |
| PlatformName = Platform->getName(); |
| |
| // Ensure that Introduced <= Deprecated <= Obsoleted (although not all |
| // of these steps are needed). |
| if (!Introduced.empty() && !Deprecated.empty() && |
| !(Introduced <= Deprecated)) { |
| S.Diag(Range.getBegin(), diag::warn_availability_version_ordering) |
| << 1 << PlatformName << Deprecated.getAsString() |
| << 0 << Introduced.getAsString(); |
| return true; |
| } |
| |
| if (!Introduced.empty() && !Obsoleted.empty() && |
| !(Introduced <= Obsoleted)) { |
| S.Diag(Range.getBegin(), diag::warn_availability_version_ordering) |
| << 2 << PlatformName << Obsoleted.getAsString() |
| << 0 << Introduced.getAsString(); |
| return true; |
| } |
| |
| if (!Deprecated.empty() && !Obsoleted.empty() && |
| !(Deprecated <= Obsoleted)) { |
| S.Diag(Range.getBegin(), diag::warn_availability_version_ordering) |
| << 2 << PlatformName << Obsoleted.getAsString() |
| << 1 << Deprecated.getAsString(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Check whether the two versions match. |
| /// |
| /// If either version tuple is empty, then they are assumed to match. If |
| /// \p BeforeIsOkay is true, then \p X can be less than or equal to \p Y. |
| static bool versionsMatch(const VersionTuple &X, const VersionTuple &Y, |
| bool BeforeIsOkay) { |
| if (X.empty() || Y.empty()) |
| return true; |
| |
| if (X == Y) |
| return true; |
| |
| if (BeforeIsOkay && X < Y) |
| return true; |
| |
| return false; |
| } |
| |
| AvailabilityAttr *Sema::mergeAvailabilityAttr( |
| NamedDecl *D, const AttributeCommonInfo &CI, IdentifierInfo *Platform, |
| bool Implicit, VersionTuple Introduced, VersionTuple Deprecated, |
| VersionTuple Obsoleted, bool IsUnavailable, StringRef Message, |
| bool IsStrict, StringRef Replacement, AvailabilityMergeKind AMK, |
| int Priority) { |
| VersionTuple MergedIntroduced = Introduced; |
| VersionTuple MergedDeprecated = Deprecated; |
| VersionTuple MergedObsoleted = Obsoleted; |
| bool FoundAny = false; |
| bool OverrideOrImpl = false; |
| switch (AMK) { |
| case AMK_None: |
| case AMK_Redeclaration: |
| OverrideOrImpl = false; |
| break; |
| |
| case AMK_Override: |
| case AMK_ProtocolImplementation: |
| case AMK_OptionalProtocolImplementation: |
| OverrideOrImpl = true; |
| break; |
| } |
| |
| if (D->hasAttrs()) { |
| AttrVec &Attrs = D->getAttrs(); |
| for (unsigned i = 0, e = Attrs.size(); i != e;) { |
| const auto *OldAA = dyn_cast<AvailabilityAttr>(Attrs[i]); |
| if (!OldAA) { |
| ++i; |
| continue; |
| } |
| |
| IdentifierInfo *OldPlatform = OldAA->getPlatform(); |
| if (OldPlatform != Platform) { |
| ++i; |
| continue; |
| } |
| |
| // If there is an existing availability attribute for this platform that |
| // has a lower priority use the existing one and discard the new |
| // attribute. |
| if (OldAA->getPriority() < Priority) |
| return nullptr; |
| |
| // If there is an existing attribute for this platform that has a higher |
| // priority than the new attribute then erase the old one and continue |
| // processing the attributes. |
| if (OldAA->getPriority() > Priority) { |
| Attrs.erase(Attrs.begin() + i); |
| --e; |
| continue; |
| } |
| |
| FoundAny = true; |
| VersionTuple OldIntroduced = OldAA->getIntroduced(); |
| VersionTuple OldDeprecated = OldAA->getDeprecated(); |
| VersionTuple OldObsoleted = OldAA->getObsoleted(); |
| bool OldIsUnavailable = OldAA->getUnavailable(); |
| |
| if (!versionsMatch(OldIntroduced, Introduced, OverrideOrImpl) || |
| !versionsMatch(Deprecated, OldDeprecated, OverrideOrImpl) || |
| !versionsMatch(Obsoleted, OldObsoleted, OverrideOrImpl) || |
| !(OldIsUnavailable == IsUnavailable || |
| (OverrideOrImpl && !OldIsUnavailable && IsUnavailable))) { |
| if (OverrideOrImpl) { |
| int Which = -1; |
| VersionTuple FirstVersion; |
| VersionTuple SecondVersion; |
| if (!versionsMatch(OldIntroduced, Introduced, OverrideOrImpl)) { |
| Which = 0; |
| FirstVersion = OldIntroduced; |
| SecondVersion = Introduced; |
| } else if (!versionsMatch(Deprecated, OldDeprecated, OverrideOrImpl)) { |
| Which = 1; |
| FirstVersion = Deprecated; |
| SecondVersion = OldDeprecated; |
| } else if (!versionsMatch(Obsoleted, OldObsoleted, OverrideOrImpl)) { |
| Which = 2; |
| FirstVersion = Obsoleted; |
| SecondVersion = OldObsoleted; |
| } |
| |
| if (Which == -1) { |
| Diag(OldAA->getLocation(), |
| diag::warn_mismatched_availability_override_unavail) |
| << AvailabilityAttr::getPrettyPlatformName(Platform->getName()) |
| << (AMK == AMK_Override); |
| } else if (Which != 1 && AMK == AMK_OptionalProtocolImplementation) { |
| // Allow different 'introduced' / 'obsoleted' availability versions |
| // on a method that implements an optional protocol requirement. It |
| // makes less sense to allow this for 'deprecated' as the user can't |
| // see if the method is 'deprecated' as 'respondsToSelector' will |
| // still return true when the method is deprecated. |
| ++i; |
| continue; |
| } else { |
| Diag(OldAA->getLocation(), |
| diag::warn_mismatched_availability_override) |
| << Which |
| << AvailabilityAttr::getPrettyPlatformName(Platform->getName()) |
| << FirstVersion.getAsString() << SecondVersion.getAsString() |
| << (AMK == AMK_Override); |
| } |
| if (AMK == AMK_Override) |
| Diag(CI.getLoc(), diag::note_overridden_method); |
| else |
| Diag(CI.getLoc(), diag::note_protocol_method); |
| } else { |
| Diag(OldAA->getLocation(), diag::warn_mismatched_availability); |
| Diag(CI.getLoc(), diag::note_previous_attribute); |
| } |
| |
| Attrs.erase(Attrs.begin() + i); |
| --e; |
| continue; |
| } |
| |
| VersionTuple MergedIntroduced2 = MergedIntroduced; |
| VersionTuple MergedDeprecated2 = MergedDeprecated; |
| VersionTuple MergedObsoleted2 = MergedObsoleted; |
| |
| if (MergedIntroduced2.empty()) |
| MergedIntroduced2 = OldIntroduced; |
| if (MergedDeprecated2.empty()) |
| MergedDeprecated2 = OldDeprecated; |
| if (MergedObsoleted2.empty()) |
| MergedObsoleted2 = OldObsoleted; |
| |
| if (checkAvailabilityAttr(*this, OldAA->getRange(), Platform, |
| MergedIntroduced2, MergedDeprecated2, |
| MergedObsoleted2)) { |
| Attrs.erase(Attrs.begin() + i); |
| --e; |
| continue; |
| } |
| |
| MergedIntroduced = MergedIntroduced2; |
| MergedDeprecated = MergedDeprecated2; |
| MergedObsoleted = MergedObsoleted2; |
| ++i; |
| } |
| } |
| |
| if (FoundAny && |
| MergedIntroduced == Introduced && |
| MergedDeprecated == Deprecated && |
| MergedObsoleted == Obsoleted) |
| return nullptr; |
| |
| // Only create a new attribute if !OverrideOrImpl, but we want to do |
| // the checking. |
| if (!checkAvailabilityAttr(*this, CI.getRange(), Platform, MergedIntroduced, |
| MergedDeprecated, MergedObsoleted) && |
| !OverrideOrImpl) { |
| auto *Avail = ::new (Context) AvailabilityAttr( |
| Context, CI, Platform, Introduced, Deprecated, Obsoleted, IsUnavailable, |
| Message, IsStrict, Replacement, Priority); |
| Avail->setImplicit(Implicit); |
| return Avail; |
| } |
| return nullptr; |
| } |
| |
| static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { |
| if (isa<UsingDecl, UnresolvedUsingTypenameDecl, UnresolvedUsingValueDecl>( |
| D)) { |
| S.Diag(AL.getRange().getBegin(), diag::warn_deprecated_ignored_on_using) |
| << AL; |
| return; |
| } |
| |
| if (!AL.checkExactlyNumArgs(S, 1)) |
| return; |
| IdentifierLoc *Platform = AL.getArgAsIdent(0); |
| |
| IdentifierInfo *II = Platform->Ident; |
| if (AvailabilityAttr::getPrettyPlatformName(II->getName()).empty()) |
| S.Diag(Platform->Loc, diag::warn_availability_unknown_platform) |
| << Platform->Ident; |
| |
| auto *ND = dyn_cast<NamedDecl>(D); |
| if (!ND) // We warned about this already, so just return. |
| return; |
| |
| AvailabilityChange Introduced = AL.getAvailabilityIntroduced(); |
| AvailabilityChange Deprecated = AL.getAvailabilityDeprecated(); |
| AvailabilityChange Obsoleted = AL.getAvailabilityObsoleted(); |
| bool IsUnavailable = AL.getUnavailableLoc().isValid(); |
| bool IsStrict = AL.getStrictLoc().isValid(); |
| StringRef Str; |
| if (const auto *SE = dyn_cast_or_null<StringLiteral>(AL.getMessageExpr())) |
| Str = SE->getString(); |
| StringRef Replacement; |
| if (const auto *SE = dyn_cast_or_null<StringLiteral>(AL.getReplacementExpr())) |
| Replacement = SE->getString(); |
| |
| if (II->isStr("swift")) { |
| if (Introduced.isValid() || Obsoleted.isValid() || |
| (!IsUnavailable && !Deprecated.isValid())) { |
| S.Diag(AL.getLoc(), |
| diag::warn_availability_swift_unavailable_deprecated_only); |
| return; |
| } |
| } |
| |
| if (II->isStr("fuchsia")) { |
| Optional<unsigned> Min, Sub; |
| if ((Min = Introduced.Version.getMinor()) || |
| (Sub = Introduced.Version.getSubminor())) { |
| S.Diag(AL.getLoc(), diag::warn_availability_fuchsia_unavailable_minor); |
| return; |
| } |
| } |
| |
| int PriorityModifier = AL.isPragmaClangAttribute() |
| ? Sema::AP_PragmaClangAttribute |
| : Sema::AP_Explicit; |
| AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr( |
| ND, AL, II, false /*Implicit*/, Introduced.Version, Deprecated.Version, |
| Obsoleted.Version, IsUnavailable, Str, IsStrict, Replacement, |
| Sema::AMK_None, PriorityModifier); |
| if (NewAttr) |
| D->addAttr(NewAttr); |
| |
| // Transcribe "ios" to "watchos" (and add a new attribute) if the versioning |
| // matches before the start of the watchOS platform. |
| if (S.Context.getTargetInfo().getTriple().isWatchOS()) { |
| IdentifierInfo *NewII = nullptr; |
| if (II->getName() == "ios") |
| NewII = &S.Context.Idents.get("watchos"); |
| else if (II->getName() == "ios_app_extension") |
| NewII = &S.Context.Idents.get("watchos_app_extension"); |
| |
| if (NewII) { |
| auto adjustWatchOSVersion = [](VersionTuple Version) -> VersionTuple { |
| if (Version.empty()) |
| return Version; |
| auto Major = Version.getMajor(); |
| auto NewMajor = Major >= 9 ? Major - 7 : 0; |
| if (NewMajor >= 2) { |
| if (Version.getMinor().hasValue()) { |
| if (Version.getSubminor().hasValue()) |
| return VersionTuple(NewMajor, Version.getMinor().getValue(), |
| Version.getSubminor().getValue()); |
| else |
| return VersionTuple(NewMajor, Version.getMinor().getValue()); |
| } |
| return VersionTuple(NewMajor); |
| } |
| |
| return VersionTuple(2, 0); |
| }; |
| |
| auto NewIntroduced = adjustWatchOSVersion(Introduced.Version); |
| auto NewDeprecated = adjustWatchOSVersion(Deprecated.Version); |
| auto NewObsoleted = adjustWatchOSVersion(Obsoleted.Version); |
| |
| AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr( |
| ND, AL, NewII, tru
|