| //===--- SemaDeclAttr.cpp - Declaration Attribute Handling ----------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // 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/Basic/CharInfo.h" |
| #include "clang/Basic/SourceManager.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/Scope.h" |
| #include "clang/Sema/SemaInternal.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/Support/MathExtras.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); |
| } |
| |
| /// \brief 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 BlockDecl *BD = dyn_cast<BlockDecl>(D)) |
| return BD->getNumParams(); |
| return cast<ObjCMethodDecl>(D)->param_size(); |
| } |
| |
| static QualType getFunctionOrMethodParamType(const Decl *D, unsigned Idx) { |
| if (const FunctionType *FnTy = D->getFunctionType()) |
| return cast<FunctionProtoType>(FnTy)->getParamType(Idx); |
| if (const BlockDecl *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 (const auto *FD = dyn_cast<FunctionDecl>(D)) |
| return FD->getParamDecl(Idx)->getSourceRange(); |
| if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) |
| return MD->parameters()[Idx]->getSourceRange(); |
| if (const auto *BD = dyn_cast<BlockDecl>(D)) |
| return BD->getParamDecl(Idx)->getSourceRange(); |
| return SourceRange(); |
| } |
| |
| static QualType getFunctionOrMethodResultType(const Decl *D) { |
| if (const FunctionType *FnTy = D->getFunctionType()) |
| return cast<FunctionType>(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()) { |
| const FunctionProtoType *proto = cast<FunctionProtoType>(FnTy); |
| return proto->isVariadic(); |
| } |
| if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) |
| return BD->isVariadic(); |
| |
| return cast<ObjCMethodDecl>(D)->isVariadic(); |
| } |
| |
| static bool isInstanceMethod(const Decl *D) { |
| if (const CXXMethodDecl *MethodDecl = dyn_cast<CXXMethodDecl>(D)) |
| return MethodDecl->isInstance(); |
| return false; |
| } |
| |
| static inline bool isNSStringType(QualType T, ASTContext &Ctx) { |
| const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); |
| if (!PT) |
| return false; |
| |
| ObjCInterfaceDecl *Cls = PT->getObjectType()->getInterface(); |
| if (!Cls) |
| return false; |
| |
| IdentifierInfo* ClsName = Cls->getIdentifier(); |
| |
| // 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 PointerType *PT = T->getAs<PointerType>(); |
| if (!PT) |
| return false; |
| |
| const RecordType *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 AttributeList &Attr) { |
| // FIXME: Include the type in the argument list. |
| return Attr.getNumArgs() + Attr.hasParsedType(); |
| } |
| |
| template <typename Compare> |
| static bool checkAttributeNumArgsImpl(Sema &S, const AttributeList &Attr, |
| unsigned Num, unsigned Diag, |
| Compare Comp) { |
| if (Comp(getNumAttributeArgs(Attr), Num)) { |
| S.Diag(Attr.getLoc(), Diag) << Attr.getName() << Num; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /// \brief Check if the attribute has exactly as many args as Num. May |
| /// output an error. |
| static bool checkAttributeNumArgs(Sema &S, const AttributeList &Attr, |
| unsigned Num) { |
| return checkAttributeNumArgsImpl(S, Attr, Num, |
| diag::err_attribute_wrong_number_arguments, |
| std::not_equal_to<unsigned>()); |
| } |
| |
| /// \brief Check if the attribute has at least as many args as Num. May |
| /// output an error. |
| static bool checkAttributeAtLeastNumArgs(Sema &S, const AttributeList &Attr, |
| unsigned Num) { |
| return checkAttributeNumArgsImpl(S, Attr, Num, |
| diag::err_attribute_too_few_arguments, |
| std::less<unsigned>()); |
| } |
| |
| /// \brief Check if the attribute has at most as many args as Num. May |
| /// output an error. |
| static bool checkAttributeAtMostNumArgs(Sema &S, const AttributeList &Attr, |
| unsigned Num) { |
| return checkAttributeNumArgsImpl(S, Attr, Num, |
| diag::err_attribute_too_many_arguments, |
| 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. |
| 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(getAttrLoc(Attr), diag::err_attribute_argument_n_type) |
| << getAttrName(Attr) << Idx << AANT_ArgumentIntegerConstant |
| << Expr->getSourceRange(); |
| else |
| S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_type) |
| << getAttrName(Attr) << AANT_ArgumentIntegerConstant |
| << Expr->getSourceRange(); |
| return false; |
| } |
| |
| if (!I.isIntN(32)) { |
| S.Diag(Expr->getExprLoc(), diag::err_ice_too_large) |
| << I.toString(10, false) << 32 << /* Unsigned */ 1; |
| return false; |
| } |
| |
| Val = (uint32_t)I.getZExtValue(); |
| return true; |
| } |
| |
| /// \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. |
| 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; |
| |
| 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) |
| << I.toString(10, false) << 32 << /* Unsigned */ 0; |
| return false; |
| } |
| |
| Val = UVal; |
| return true; |
| } |
| |
| /// \brief Diagnose mutually exclusive attributes when present on a given |
| /// declaration. Returns true if diagnosed. |
| template <typename AttrTy> |
| static bool checkAttrMutualExclusion(Sema &S, Decl *D, SourceRange Range, |
| IdentifierInfo *Ident) { |
| if (AttrTy *A = D->getAttr<AttrTy>()) { |
| S.Diag(Range.getBegin(), diag::err_attributes_are_not_compatible) << Ident |
| << A; |
| S.Diag(A->getLocation(), diag::note_conflicting_attribute); |
| return true; |
| } |
| return false; |
| } |
| |
| /// \brief 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 &Attr, unsigned AttrArgNum, |
| const Expr *IdxExpr, uint64_t &Idx, bool AllowImplicitThis = 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; |
| |
| llvm::APSInt IdxInt; |
| if (IdxExpr->isTypeDependent() || IdxExpr->isValueDependent() || |
| !IdxExpr->isIntegerConstantExpr(IdxInt, S.Context)) { |
| 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(getAttrLoc(Attr), diag::err_attribute_argument_out_of_bounds) |
| << getAttrName(Attr) << AttrArgNum << IdxExpr->getSourceRange(); |
| return false; |
| } |
| Idx--; // Convert to zero-based. |
| if (HasImplicitThisParam && !AllowImplicitThis) { |
| if (Idx == 0) { |
| S.Diag(getAttrLoc(Attr), |
| diag::err_attribute_invalid_implicit_this_argument) |
| << getAttrName(Attr) << IdxExpr->getSourceRange(); |
| return false; |
| } |
| --Idx; |
| } |
| |
| return true; |
| } |
| |
| /// \brief 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 AttributeList &Attr, |
| unsigned ArgNum, StringRef &Str, |
| SourceLocation *ArgLocation) { |
| // Look for identifiers. If we have one emit a hint to fix it to a literal. |
| if (Attr.isArgIdent(ArgNum)) { |
| IdentifierLoc *Loc = Attr.getArgAsIdent(ArgNum); |
| Diag(Loc->Loc, diag::err_attribute_argument_type) |
| << Attr.getName() << 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 = Attr.getArgAsExpr(ArgNum); |
| StringLiteral *Literal = dyn_cast<StringLiteral>(ArgExpr->IgnoreParenCasts()); |
| if (ArgLocation) |
| *ArgLocation = ArgExpr->getLocStart(); |
| |
| if (!Literal || !Literal->isAscii()) { |
| Diag(ArgExpr->getLocStart(), diag::err_attribute_argument_type) |
| << Attr.getName() << AANT_ArgumentString; |
| return false; |
| } |
| |
| Str = Literal->getString(); |
| return true; |
| } |
| |
| /// \brief Applies the given attribute to the Decl without performing any |
| /// additional semantic checking. |
| template <typename AttrType> |
| static void handleSimpleAttribute(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| D->addAttr(::new (S.Context) AttrType(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| template <typename AttrType> |
| static void handleSimpleAttributeWithExclusions(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| handleSimpleAttribute<AttrType>(S, D, Attr); |
| } |
| |
| /// \brief Applies the given attribute to the Decl so long as the Decl doesn't |
| /// already have one of the given incompatible attributes. |
| template <typename AttrType, typename IncompatibleAttrType, |
| typename... IncompatibleAttrTypes> |
| static void handleSimpleAttributeWithExclusions(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| if (checkAttrMutualExclusion<IncompatibleAttrType>(S, D, Attr.getRange(), |
| Attr.getName())) |
| return; |
| handleSimpleAttributeWithExclusions<AttrType, IncompatibleAttrTypes...>(S, D, |
| Attr); |
| } |
| |
| /// \brief 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) { |
| DeclContextLookupResult Res1 = RT->getDecl()->lookup( |
| S.Context.DeclarationNames.getCXXOperatorName(OO_Star)); |
| if (Res1.empty()) |
| return false; |
| |
| DeclContextLookupResult Res2 = RT->getDecl()->lookup( |
| S.Context.DeclarationNames.getCXXOperatorName(OO_Arrow)); |
| if (Res2.empty()) |
| return false; |
| |
| return true; |
| } |
| |
| /// \brief 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 AttributeList &Attr) { |
| const ValueDecl *vd = cast<ValueDecl>(D); |
| QualType QT = vd->getType(); |
| if (QT->isAnyPointerType()) |
| return true; |
| |
| if (const RecordType *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(Attr.getLoc(), diag::warn_thread_attribute_decl_not_pointer) |
| << Attr.getName() << QT; |
| return false; |
| } |
| |
| /// \brief 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 RecordType *RT = QT->getAs<RecordType>()) |
| return RT; |
| |
| // Now check if we point to record type. |
| if (const PointerType *PT = QT->getAs<PointerType>()) |
| return PT->getPointeeType()->getAs<RecordType>(); |
| |
| return nullptr; |
| } |
| |
| 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; |
| |
| // Check if the record itself has a capability. |
| RecordDecl *RD = RT->getDecl(); |
| if (RD->hasAttr<CapabilityAttr>()) |
| return true; |
| |
| // Else check if any base classes have a capability. |
| if (CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(RD)) { |
| CXXBasePaths BPaths(false, false); |
| if (CRD->lookupInBases([](const CXXBaseSpecifier *BS, CXXBasePath &) { |
| const auto *Type = BS->getType()->getAs<RecordType>(); |
| return Type->getDecl()->hasAttr<CapabilityAttr>(); |
| }, BPaths)) |
| return true; |
| } |
| return false; |
| } |
| |
| 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()); |
| } |
| |
| /// \brief 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 AttributeList &Attr, |
| SmallVectorImpl<Expr *> &Args, |
| int Sidx = 0, |
| bool ParamIdxOk = false) { |
| for (unsigned Idx = Sidx; Idx < Attr.getNumArgs(); ++Idx) { |
| Expr *ArgExp = Attr.getArgAsExpr(Idx); |
| |
| if (ArgExp->isTypeDependent()) { |
| // FIXME -- need to check this again on template instantiation |
| Args.push_back(ArgExp); |
| continue; |
| } |
| |
| if (StringLiteral *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(Attr.getLoc(), diag::warn_thread_attribute_ignored) << |
| Attr.getName(); |
| 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 (UnaryOperator *UOp = dyn_cast<UnaryOperator>(ArgExp)) |
| if (UOp->getOpcode() == UO_AddrOf) |
| if (DeclRefExpr *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) { |
| FunctionDecl *FD = dyn_cast<FunctionDecl>(D); |
| IntegerLiteral *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(Attr.getLoc(), diag::err_attribute_argument_out_of_range) |
| << Attr.getName() << 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(Attr.getLoc(), diag::warn_thread_attribute_argument_not_lockable) |
| << Attr.getName() << ArgTy; |
| |
| Args.push_back(ArgExp); |
| } |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Attribute Implementations |
| //===----------------------------------------------------------------------===// |
| |
| static void handlePtGuardedVarAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| if (!threadSafetyCheckIsPointer(S, D, Attr)) |
| return; |
| |
| D->addAttr(::new (S.Context) |
| PtGuardedVarAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static bool checkGuardedByAttrCommon(Sema &S, Decl *D, |
| const AttributeList &Attr, |
| Expr* &Arg) { |
| SmallVector<Expr*, 1> Args; |
| // check that all arguments are lockable objects |
| checkAttrArgsAreCapabilityObjs(S, D, Attr, Args); |
| unsigned Size = Args.size(); |
| if (Size != 1) |
| return false; |
| |
| Arg = Args[0]; |
| |
| return true; |
| } |
| |
| static void handleGuardedByAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| Expr *Arg = nullptr; |
| if (!checkGuardedByAttrCommon(S, D, Attr, Arg)) |
| return; |
| |
| D->addAttr(::new (S.Context) GuardedByAttr(Attr.getRange(), S.Context, Arg, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handlePtGuardedByAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| Expr *Arg = nullptr; |
| if (!checkGuardedByAttrCommon(S, D, Attr, Arg)) |
| return; |
| |
| if (!threadSafetyCheckIsPointer(S, D, Attr)) |
| return; |
| |
| D->addAttr(::new (S.Context) PtGuardedByAttr(Attr.getRange(), |
| S.Context, Arg, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static bool checkAcquireOrderAttrCommon(Sema &S, Decl *D, |
| const AttributeList &Attr, |
| SmallVectorImpl<Expr *> &Args) { |
| if (!checkAttributeAtLeastNumArgs(S, Attr, 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(Attr.getLoc(), diag::warn_thread_attribute_decl_not_lockable) |
| << Attr.getName(); |
| return false; |
| } |
| |
| // Check that all arguments are lockable objects. |
| checkAttrArgsAreCapabilityObjs(S, D, Attr, Args); |
| if (Args.empty()) |
| return false; |
| |
| return true; |
| } |
| |
| static void handleAcquiredAfterAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| SmallVector<Expr*, 1> Args; |
| if (!checkAcquireOrderAttrCommon(S, D, Attr, Args)) |
| return; |
| |
| Expr **StartArg = &Args[0]; |
| D->addAttr(::new (S.Context) |
| AcquiredAfterAttr(Attr.getRange(), S.Context, |
| StartArg, Args.size(), |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleAcquiredBeforeAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| SmallVector<Expr*, 1> Args; |
| if (!checkAcquireOrderAttrCommon(S, D, Attr, Args)) |
| return; |
| |
| Expr **StartArg = &Args[0]; |
| D->addAttr(::new (S.Context) |
| AcquiredBeforeAttr(Attr.getRange(), S.Context, |
| StartArg, Args.size(), |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static bool checkLockFunAttrCommon(Sema &S, Decl *D, |
| const AttributeList &Attr, |
| SmallVectorImpl<Expr *> &Args) { |
| // zero or more arguments ok |
| // check that all arguments are lockable objects |
| checkAttrArgsAreCapabilityObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true); |
| |
| return true; |
| } |
| |
| static void handleAssertSharedLockAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| SmallVector<Expr*, 1> Args; |
| if (!checkLockFunAttrCommon(S, D, Attr, Args)) |
| return; |
| |
| unsigned Size = Args.size(); |
| Expr **StartArg = Size == 0 ? nullptr : &Args[0]; |
| D->addAttr(::new (S.Context) |
| AssertSharedLockAttr(Attr.getRange(), S.Context, StartArg, Size, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleAssertExclusiveLockAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| SmallVector<Expr*, 1> Args; |
| if (!checkLockFunAttrCommon(S, D, Attr, Args)) |
| return; |
| |
| unsigned Size = Args.size(); |
| Expr **StartArg = Size == 0 ? nullptr : &Args[0]; |
| D->addAttr(::new (S.Context) |
| AssertExclusiveLockAttr(Attr.getRange(), S.Context, |
| StartArg, Size, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| /// \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 AttrInfo &Attr, Expr *AttrArg, |
| unsigned FuncParamNo, unsigned AttrArgNo, |
| bool AllowDependentType = false) { |
| uint64_t 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 = AttrArg->getLocStart(); |
| S.Diag(SrcLoc, diag::err_attribute_integers_only) |
| << 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)) |
| return; |
| |
| const auto *FD = cast<FunctionDecl>(D); |
| if (!FD->getReturnType()->isPointerType()) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_return_pointers_only) |
| << Attr.getName(); |
| return; |
| } |
| |
| const Expr *SizeExpr = Attr.getArgAsExpr(0); |
| int SizeArgNo; |
| // Parameter indices are 1-indexed, hence Index=1 |
| if (!checkPositiveIntArgument(S, Attr, SizeExpr, SizeArgNo, /*Index=*/1)) |
| return; |
| |
| if (!checkParamIsIntegerType(S, FD, Attr, SizeArgNo, /*AttrArgNo=*/0)) |
| return; |
| |
| // Args are 1-indexed, so 0 implies that the arg was not present |
| int NumberArgNo = 0; |
| if (Attr.getNumArgs() == 2) { |
| const Expr *NumberExpr = Attr.getArgAsExpr(1); |
| // Parameter indices are 1-based, hence Index=2 |
| if (!checkPositiveIntArgument(S, Attr, NumberExpr, NumberArgNo, |
| /*Index=*/2)) |
| return; |
| |
| if (!checkParamIsIntegerType(S, FD, Attr, NumberArgNo, /*AttrArgNo=*/1)) |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) AllocSizeAttr( |
| Attr.getRange(), S.Context, SizeArgNo, NumberArgNo, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static bool checkTryLockFunAttrCommon(Sema &S, Decl *D, |
| const AttributeList &Attr, |
| SmallVectorImpl<Expr *> &Args) { |
| if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) |
| return false; |
| |
| if (!isIntOrBool(Attr.getArgAsExpr(0))) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type) |
| << Attr.getName() << 1 << AANT_ArgumentIntOrBool; |
| return false; |
| } |
| |
| // check that all arguments are lockable objects |
| checkAttrArgsAreCapabilityObjs(S, D, Attr, Args, 1); |
| |
| return true; |
| } |
| |
| static void handleSharedTrylockFunctionAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| SmallVector<Expr*, 2> Args; |
| if (!checkTryLockFunAttrCommon(S, D, Attr, Args)) |
| return; |
| |
| D->addAttr(::new (S.Context) |
| SharedTrylockFunctionAttr(Attr.getRange(), S.Context, |
| Attr.getArgAsExpr(0), |
| Args.data(), Args.size(), |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleExclusiveTrylockFunctionAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| SmallVector<Expr*, 2> Args; |
| if (!checkTryLockFunAttrCommon(S, D, Attr, Args)) |
| return; |
| |
| D->addAttr(::new (S.Context) ExclusiveTrylockFunctionAttr( |
| Attr.getRange(), S.Context, Attr.getArgAsExpr(0), Args.data(), |
| Args.size(), Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleLockReturnedAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| // check that the argument is lockable object |
| SmallVector<Expr*, 1> Args; |
| checkAttrArgsAreCapabilityObjs(S, D, Attr, Args); |
| unsigned Size = Args.size(); |
| if (Size == 0) |
| return; |
| |
| D->addAttr(::new (S.Context) |
| LockReturnedAttr(Attr.getRange(), S.Context, Args[0], |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleLocksExcludedAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) |
| return; |
| |
| // check that all arguments are lockable objects |
| SmallVector<Expr*, 1> Args; |
| checkAttrArgsAreCapabilityObjs(S, D, Attr, Args); |
| unsigned Size = Args.size(); |
| if (Size == 0) |
| return; |
| Expr **StartArg = &Args[0]; |
| |
| D->addAttr(::new (S.Context) |
| LocksExcludedAttr(Attr.getRange(), S.Context, StartArg, Size, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static bool checkFunctionConditionAttr(Sema &S, Decl *D, |
| const AttributeList &Attr, |
| Expr *&Cond, StringRef &Msg) { |
| Cond = Attr.getArgAsExpr(0); |
| if (!Cond->isTypeDependent()) { |
| ExprResult Converted = S.PerformContextuallyConvertToBool(Cond); |
| if (Converted.isInvalid()) |
| return false; |
| Cond = Converted.get(); |
| } |
| |
| if (!S.checkStringLiteralArgumentAttr(Attr, 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(Attr.getLoc(), diag::err_attr_cond_never_constant_expr) |
| << Attr.getName(); |
| for (const PartialDiagnosticAt &PDiag : Diags) |
| S.Diag(PDiag.first, PDiag.second); |
| return false; |
| } |
| return true; |
| } |
| |
| static void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| S.Diag(Attr.getLoc(), diag::ext_clang_enable_if); |
| |
| Expr *Cond; |
| StringRef Msg; |
| if (checkFunctionConditionAttr(S, D, Attr, Cond, Msg)) |
| D->addAttr(::new (S.Context) |
| EnableIfAttr(Attr.getRange(), S.Context, Cond, Msg, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| 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 AttributeList &Attr) { |
| S.Diag(Attr.getLoc(), diag::ext_clang_diagnose_if); |
| |
| Expr *Cond; |
| StringRef Msg; |
| if (!checkFunctionConditionAttr(S, D, Attr, Cond, Msg)) |
| return; |
| |
| StringRef DiagTypeStr; |
| if (!S.checkStringLiteralArgumentAttr(Attr, 2, DiagTypeStr)) |
| return; |
| |
| DiagnoseIfAttr::DiagnosticType DiagType; |
| if (!DiagnoseIfAttr::ConvertStrToDiagnosticType(DiagTypeStr, DiagType)) { |
| S.Diag(Attr.getArgAsExpr(2)->getLocStart(), |
| 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( |
| Attr.getRange(), S.Context, Cond, Msg, DiagType, ArgDependent, cast<NamedDecl>(D), |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handlePassObjectSizeAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| if (D->hasAttr<PassObjectSizeAttr>()) { |
| S.Diag(D->getLocStart(), diag::err_attribute_only_once_per_parameter) |
| << Attr.getName(); |
| return; |
| } |
| |
| Expr *E = Attr.getArgAsExpr(0); |
| uint32_t Type; |
| if (!checkUInt32Argument(S, Attr, 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->getLocStart(), diag::err_attribute_argument_outof_range) |
| << Attr.getName() << 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->getLocStart(), diag::err_attribute_pointers_only) |
| << Attr.getName() << 1; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| PassObjectSizeAttr(Attr.getRange(), S.Context, (int)Type, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleConsumableAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| ConsumableAttr::ConsumedState DefaultState; |
| |
| if (Attr.isArgIdent(0)) { |
| IdentifierLoc *IL = Attr.getArgAsIdent(0); |
| if (!ConsumableAttr::ConvertStrToConsumedState(IL->Ident->getName(), |
| DefaultState)) { |
| S.Diag(IL->Loc, diag::warn_attribute_type_not_supported) |
| << Attr.getName() << IL->Ident; |
| return; |
| } |
| } else { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) |
| << Attr.getName() << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| ConsumableAttr(Attr.getRange(), S.Context, DefaultState, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static bool checkForConsumableClass(Sema &S, const CXXMethodDecl *MD, |
| const AttributeList &Attr) { |
| ASTContext &CurrContext = S.getASTContext(); |
| QualType ThisType = MD->getThisType(CurrContext)->getPointeeType(); |
| |
| if (const CXXRecordDecl *RD = ThisType->getAsCXXRecordDecl()) { |
| if (!RD->hasAttr<ConsumableAttr>()) { |
| S.Diag(Attr.getLoc(), diag::warn_attr_on_unconsumable_class) << |
| RD->getNameAsString(); |
| |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| static void handleCallableWhenAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) |
| return; |
| |
| if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr)) |
| return; |
| |
| SmallVector<CallableWhenAttr::ConsumedState, 3> States; |
| for (unsigned ArgIndex = 0; ArgIndex < Attr.getNumArgs(); ++ArgIndex) { |
| CallableWhenAttr::ConsumedState CallableState; |
| |
| StringRef StateString; |
| SourceLocation Loc; |
| if (Attr.isArgIdent(ArgIndex)) { |
| IdentifierLoc *Ident = Attr.getArgAsIdent(ArgIndex); |
| StateString = Ident->Ident->getName(); |
| Loc = Ident->Loc; |
| } else { |
| if (!S.checkStringLiteralArgumentAttr(Attr, ArgIndex, StateString, &Loc)) |
| return; |
| } |
| |
| if (!CallableWhenAttr::ConvertStrToConsumedState(StateString, |
| CallableState)) { |
| S.Diag(Loc, diag::warn_attribute_type_not_supported) |
| << Attr.getName() << StateString; |
| return; |
| } |
| |
| States.push_back(CallableState); |
| } |
| |
| D->addAttr(::new (S.Context) |
| CallableWhenAttr(Attr.getRange(), S.Context, States.data(), |
| States.size(), Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleParamTypestateAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| ParamTypestateAttr::ConsumedState ParamState; |
| |
| if (Attr.isArgIdent(0)) { |
| IdentifierLoc *Ident = Attr.getArgAsIdent(0); |
| StringRef StateString = Ident->Ident->getName(); |
| |
| if (!ParamTypestateAttr::ConvertStrToConsumedState(StateString, |
| ParamState)) { |
| S.Diag(Ident->Loc, diag::warn_attribute_type_not_supported) |
| << Attr.getName() << StateString; |
| return; |
| } |
| } else { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) << |
| Attr.getName() << 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(Attr.getLoc(), diag::warn_return_state_for_unconsumable_type) << |
| // ReturnType.getAsString(); |
| // return; |
| //} |
| |
| D->addAttr(::new (S.Context) |
| ParamTypestateAttr(Attr.getRange(), S.Context, ParamState, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleReturnTypestateAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| ReturnTypestateAttr::ConsumedState ReturnState; |
| |
| if (Attr.isArgIdent(0)) { |
| IdentifierLoc *IL = Attr.getArgAsIdent(0); |
| if (!ReturnTypestateAttr::ConvertStrToConsumedState(IL->Ident->getName(), |
| ReturnState)) { |
| S.Diag(IL->Loc, diag::warn_attribute_type_not_supported) |
| << Attr.getName() << IL->Ident; |
| return; |
| } |
| } else { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) << |
| Attr.getName() << 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(S.getASTContext())->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(Attr.getRange(), S.Context, ReturnState, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleSetTypestateAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr)) |
| return; |
| |
| SetTypestateAttr::ConsumedState NewState; |
| if (Attr.isArgIdent(0)) { |
| IdentifierLoc *Ident = Attr.getArgAsIdent(0); |
| StringRef Param = Ident->Ident->getName(); |
| if (!SetTypestateAttr::ConvertStrToConsumedState(Param, NewState)) { |
| S.Diag(Ident->Loc, diag::warn_attribute_type_not_supported) |
| << Attr.getName() << Param; |
| return; |
| } |
| } else { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) << |
| Attr.getName() << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| SetTypestateAttr(Attr.getRange(), S.Context, NewState, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleTestTypestateAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr)) |
| return; |
| |
| TestTypestateAttr::ConsumedState TestState; |
| if (Attr.isArgIdent(0)) { |
| IdentifierLoc *Ident = Attr.getArgAsIdent(0); |
| StringRef Param = Ident->Ident->getName(); |
| if (!TestTypestateAttr::ConvertStrToConsumedState(Param, TestState)) { |
| S.Diag(Ident->Loc, diag::warn_attribute_type_not_supported) |
| << Attr.getName() << Param; |
| return; |
| } |
| } else { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) << |
| Attr.getName() << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| TestTypestateAttr(Attr.getRange(), S.Context, TestState, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D, |
| const AttributeList &Attr) { |
| // 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 AttributeList &Attr) { |
| if (TagDecl *TD = dyn_cast<TagDecl>(D)) |
| TD->addAttr(::new (S.Context) PackedAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| else if (FieldDecl *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(Attr.getLoc(), diag::warn_attribute_ignored_for_field_of_type) |
| << Attr.getName() << FD->getType(); |
| else |
| FD->addAttr(::new (S.Context) PackedAttr( |
| Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); |
| } else { |
| // Report warning about changed offset in the newer compiler versions. |
| if (BitfieldByteAligned) |
| S.Diag(Attr.getLoc(), diag::warn_attribute_packed_for_bitfield); |
| |
| FD->addAttr(::new (S.Context) PackedAttr( |
| Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); |
| } |
| |
| } else |
| S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); |
| } |
| |
| static bool checkIBOutletCommon(Sema &S, Decl *D, const AttributeList &Attr) { |
| // 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 ObjCIvarDecl *VD = dyn_cast<ObjCIvarDecl>(D)) { |
| if (!VD->getType()->getAs<ObjCObjectPointerType>()) { |
| S.Diag(Attr.getLoc(), diag::warn_iboutlet_object_type) |
| << Attr.getName() << VD->getType() << 0; |
| return false; |
| } |
| } |
| else if (const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D)) { |
| if (!PD->getType()->getAs<ObjCObjectPointerType>()) { |
| S.Diag(Attr.getLoc(), diag::warn_iboutlet_object_type) |
| << Attr.getName() << PD->getType() << 1; |
| return false; |
| } |
| } |
| else { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_iboutlet) << Attr.getName(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static void handleIBOutlet(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (!checkIBOutletCommon(S, D, Attr)) |
| return; |
| |
| D->addAttr(::new (S.Context) |
| IBOutletAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleIBOutletCollection(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| |
| // The iboutletcollection attribute can have zero or one arguments. |
| if (Attr.getNumArgs() > 1) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) |
| << Attr.getName() << 1; |
| return; |
| } |
| |
| if (!checkIBOutletCommon(S, D, Attr)) |
| return; |
| |
| ParsedType PT; |
| |
| if (Attr.hasParsedType()) |
| PT = Attr.getTypeArg(); |
| else { |
| PT = S.getTypeName(S.Context.Idents.get("NSObject"), Attr.getLoc(), |
| S.getScopeForContext(D->getDeclContext()->getParent())); |
| if (!PT) { |
| S.Diag(Attr.getLoc(), diag::err_iboutletcollection_type) << "NSObject"; |
| return; |
| } |
| } |
| |
| TypeSourceInfo *QTLoc = nullptr; |
| QualType QT = S.GetTypeFromParser(PT, &QTLoc); |
| if (!QTLoc) |
| QTLoc = S.Context.getTrivialTypeSourceInfo(QT, Attr.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(Attr.getLoc(), |
| QT->isBuiltinType() ? diag::err_iboutletcollection_builtintype |
| : diag::err_iboutletcollection_type) << QT; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| IBOutletCollectionAttr(Attr.getRange(), S.Context, QTLoc, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| 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 AttributeList &Attr, |
| SourceRange AttrParmRange, |
| SourceRange TypeRange, |
| bool isReturnValue = false) { |
| if (!S.isValidPointerAttrType(T)) { |
| if (isReturnValue) |
| S.Diag(Attr.getLoc(), diag::warn_attribute_return_pointers_only) |
| << Attr.getName() << AttrParmRange << TypeRange; |
| else |
| S.Diag(Attr.getLoc(), diag::warn_attribute_pointers_only) |
| << Attr.getName() << AttrParmRange << TypeRange << 0; |
| return false; |
| } |
| return true; |
| } |
| |
| static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| SmallVector<unsigned, 8> NonNullArgs; |
| for (unsigned I = 0; I < Attr.getNumArgs(); ++I) { |
| Expr *Ex = Attr.getArgAsExpr(I); |
| uint64_t Idx; |
| if (!checkFunctionOrMethodParameterIndex(S, D, Attr, I + 1, Ex, Idx)) |
| return; |
| |
| // Is the function argument a pointer type? |
| if (Idx < getFunctionOrMethodNumParams(D) && |
| !attrNonNullArgCheck(S, getFunctionOrMethodParamType(D, Idx), Attr, |
| Ex->getSourceRange(), |
| getFunctionOrMethodParamRange(D, Idx))) |
| 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() && Attr.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(Attr.getLoc(), diag::warn_attribute_nonnull_no_pointers); |
| } |
| |
| unsigned *Start = NonNullArgs.data(); |
| unsigned Size = NonNullArgs.size(); |
| llvm::array_pod_sort(Start, Start + Size); |
| D->addAttr(::new (S.Context) |
| NonNullAttr(Attr.getRange(), S.Context, Start, Size, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleNonNullAttrParameter(Sema &S, ParmVarDecl *D, |
| const AttributeList &Attr) { |
| if (Attr.getNumArgs() > 0) { |
| if (D->getFunctionType()) { |
| handleNonNullAttr(S, D, Attr); |
| } else { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_nonnull_parm_no_args) |
| << D->getSourceRange(); |
| } |
| return; |
| } |
| |
| // Is the argument a pointer type? |
| if (!attrNonNullArgCheck(S, D->getType(), Attr, SourceRange(), |
| D->getSourceRange())) |
| return; |
| |
| D->addAttr(::new (S.Context) |
| NonNullAttr(Attr.getRange(), S.Context, nullptr, 0, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleReturnsNonNullAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| QualType ResultType = getFunctionOrMethodResultType(D); |
| SourceRange SR = getFunctionOrMethodResultSourceRange(D); |
| if (!attrNonNullArgCheck(S, ResultType, Attr, SourceRange(), SR, |
| /* isReturnValue */ true)) |
| return; |
| |
| D->addAttr(::new (S.Context) |
| ReturnsNonNullAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleNoEscapeAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (D->isInvalidDecl()) |
| return; |
| |
| // noescape only applies to pointer types. |
| QualType T = cast<ParmVarDecl>(D)->getType(); |
| if (!S.isValidPointerAttrType(T, /* RefOkay */ true)) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_pointers_only) |
| << Attr.getName() << Attr.getRange() << 0; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) NoEscapeAttr( |
| Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleAssumeAlignedAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| Expr *E = Attr.getArgAsExpr(0), |
| *OE = Attr.getNumArgs() > 1 ? Attr.getArgAsExpr(1) : nullptr; |
| S.AddAssumeAlignedAttr(Attr.getRange(), D, E, OE, |
| 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); |
| SourceRange SR = getFunctionOrMethodResultSourceRange(D); |
| |
| AssumeAlignedAttr TmpAttr(AttrRange, Context, E, OE, SpellingListIndex); |
| SourceLocation AttrLoc = AttrRange.getBegin(); |
| |
| if (!isValidPointerAttrType(ResultType, /* RefOkay */ true)) { |
| Diag(AttrLoc, diag::warn_attribute_return_pointers_refs_only) |
| << &TmpAttr << AttrRange << SR; |
| return; |
| } |
| |
| if (!E->isValueDependent()) { |
| llvm::APSInt I(64); |
| if (!E->isIntegerConstantExpr(I, 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 (OE) { |
| if (!OE->isValueDependent()) { |
| llvm::APSInt I(64); |
| if (!OE->isIntegerConstantExpr(I, Context)) { |
| Diag(AttrLoc, diag::err_attribute_argument_n_type) |
| << &TmpAttr << 2 << AANT_ArgumentIntegerConstant |
| << OE->getSourceRange(); |
| return; |
| } |
| } |
| } |
| |
| D->addAttr(::new (Context) |
| 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) { |
| 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 AttributeList &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.getName() << 1 << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| // Figure out our Kind. |
| OwnershipAttr::OwnershipKind K = |
| OwnershipAttr(AL.getLoc(), S.Context, nullptr, nullptr, 0, |
| AL.getAttributeSpellingListIndex()).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.getName() << 2; |
| return; |
| } |
| break; |
| case OwnershipAttr::Returns: |
| if (AL.getNumArgs() > 2) { |
| S.Diag(AL.getLoc(), diag::err_attribute_too_many_arguments) |
| << AL.getName() << 1; |
| return; |
| } |
| break; |
| } |
| |
| IdentifierInfo *Module = AL.getArgAsIdent(0)->Ident; |
| |
| StringRef ModuleName = Module->getName(); |
| if (normalizeName(ModuleName)) { |
| Module = &S.PP.getIdentifierTable().get(ModuleName); |
| } |
| |
| SmallVector<unsigned, 8> OwnershipArgs; |
| for (unsigned i = 1; i < AL.getNumArgs(); ++i) { |
| Expr *Ex = AL.getArgAsExpr(i); |
| uint64_t Idx; |
| if (!checkFunctionOrMethodParameterIndex(S, D, AL, i, Ex, Idx)) |
| return; |
| |
| // Is the function argument a pointer type? |
| QualType T = getFunctionOrMethodParamType(D, Idx); |
| 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.getName() << 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.getName() << I; |
| return; |
| } else if (K == OwnershipAttr::Returns && |
| I->getOwnKind() == OwnershipAttr::Returns) { |
| // A returns attribute conflicts with any other returns attribute using |
| // a different index. Note, diagnostic reporting is 1-based, but stored |
| // argument indexes are 0-based. |
| if (std::find(I->args_begin(), I->args_end(), Idx) == I->args_end()) { |
| S.Diag(I->getLocation(), diag::err_ownership_returns_index_mismatch) |
| << *(I->args_begin()) + 1; |
| if (I->args_size()) |
| S.Diag(AL.getLoc(), diag::note_ownership_returns_index_mismatch) |
| << (unsigned)Idx + 1 << Ex->getSourceRange(); |
| return; |
| } |
| } |
| } |
| OwnershipArgs.push_back(Idx); |
| } |
| |
| unsigned* start = OwnershipArgs.data(); |
| unsigned size = OwnershipArgs.size(); |
| llvm::array_pod_sort(start, start + size); |
| |
| D->addAttr(::new (S.Context) |
| OwnershipAttr(AL.getLoc(), S.Context, Module, start, size, |
| AL.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleWeakRefAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| // Check the attribute arguments. |
| if (Attr.getNumArgs() > 1) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) |
| << Attr.getName() << 1; |
| return; |
| } |
| |
| NamedDecl *nd = cast<NamedDecl>(D); |
| |
| // 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(Attr.getLoc(), diag::err_attribute_weakref_not_global_context) |
| << nd; |
| 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 (Attr.getNumArgs() && S.checkStringLiteralArgumentAttr(Attr, 0, Str)) |
| // GCC will accept anything as the argument of weakref. Should we |
| // check for an existing decl? |
| D->addAttr(::new (S.Context) AliasAttr(Attr.getRange(), S.Context, Str, |
| Attr.getAttributeSpellingListIndex())); |
| |
| D->addAttr(::new (S.Context) |
| WeakRefAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleIFuncAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| StringRef Str; |
| if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str)) |
| return; |
| |
| // Aliases should be on declarations, not definitions. |
| const auto *FD = cast<FunctionDecl>(D); |
| if (FD->isThisDeclarationADefinition()) { |
| S.Diag(Attr.getLoc(), diag::err_alias_is_definition) << FD << 1; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) IFuncAttr(Attr.getRange(), S.Context, Str, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleAliasAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| StringRef Str; |
| if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str)) |
| return; |
| |
| if (S.Context.getTargetInfo().getTriple().isOSDarwin()) { |
| S.Diag(Attr.getLoc(), diag::err_alias_not_supported_on_darwin); |
| return; |
| } |
| if (S.Context.getTargetInfo().getTriple().isNVPTX()) { |
| S.Diag(Attr.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(Attr.getLoc(), diag::err_alias_is_definition) << FD << 0; |
| return; |
| } |
| } else { |
| const auto *VD = cast<VarDecl>(D); |
| if (VD->isThisDeclarationADefinition() && VD->isExternallyVisible()) { |
| S.Diag(Attr.getLoc(), diag::err_alias_is_definition) << VD << 0; |
| return; |
| } |
| } |
| |
| // FIXME: check if target symbol exists in current file |
| |
| D->addAttr(::new (S.Context) AliasAttr(Attr.getRange(), S.Context, Str, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleColdAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (checkAttrMutualExclusion<HotAttr>(S, D, Attr.getRange(), Attr.getName())) |
| return; |
| |
| D->addAttr(::new (S.Context) ColdAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleHotAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (checkAttrMutualExclusion<ColdAttr>(S, D, Attr.getRange(), Attr.getName())) |
| return; |
| |
| D->addAttr(::new (S.Context) HotAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleTLSModelAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| StringRef Model; |
| SourceLocation LiteralLoc; |
| // Check that it is a string. |
| if (!S.checkStringLiteralArgumentAttr(Attr, 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; |
| } |
| |
| D->addAttr(::new (S.Context) |
| TLSModelAttr(Attr.getRange(), S.Context, Model, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleRestrictAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| QualType ResultType = getFunctionOrMethodResultType(D); |
| if (ResultType->isAnyPointerType() || ResultType->isBlockPointerType()) { |
| D->addAttr(::new (S.Context) RestrictAttr( |
| Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); |
| return; |
| } |
| |
| S.Diag(Attr.getLoc(), diag::warn_attribute_return_pointers_only) |
| << Attr.getName() << getFunctionOrMethodResultSourceRange(D); |
| } |
| |
| static void handleCommonAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (S.LangOpts.CPlusPlus) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_not_supported_in_lang) |
| << Attr.getName() << AttributeLangSupport::Cpp; |
| return; |
| } |
| |
| if (CommonAttr *CA = S.mergeCommonAttr(D, Attr.getRange(), Attr.getName(), |
| Attr.getAttributeSpellingListIndex())) |
| D->addAttr(CA); |
| } |
| |
| static void handleNakedAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (checkAttrMutualExclusion<DisableTailCallsAttr>(S, D, Attr.getRange(), |
| 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())); |
| } |
| |
| static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &Attrs) { |
| if (hasDeclarator(D)) return; |
| |
| if (S.CheckNoReturnAttr(Attrs)) |
| return; |
| |
| if (!isa<ObjCMethodDecl>(D)) { |
| S.Diag(Attrs.getLoc(), diag::warn_attribute_wrong_decl_type) |
| << Attrs.getName() << ExpectedFunctionOrMethod; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) NoReturnAttr( |
| Attrs.getRange(), S.Context, Attrs.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 &Attrs) { |
| if (!checkAttributeNumArgs(*this, Attrs, 0)) { |
| Attrs.setInvalid(); |
| return true; |
| } |
| |
| 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) { |
| |
| // 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(Attr.getLoc(), |
| Attr.isCXX11Attribute() ? diag::err_attribute_wrong_decl_type |
| : diag::warn_attribute_wrong_decl_type) |
| << Attr.getName() << ExpectedFunctionMethodOrBlock; |
| return; |
| } |
| } |
| |
| D->addAttr(::new (S.Context) |
| AnalyzerNoReturnAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| // PS3 PPU-specific. |
| static void handleVecReturnAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| /* |
| 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(Attr.getLoc(), diag::err_repeat_attribute) << A; |
| return; |
| } |
| |
| RecordDecl *record = cast<RecordDecl>(D); |
| int count = 0; |
| |
| if (!isa<CXXRecordDecl>(record)) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_vecreturn_only_vector_member); |
| return; |
| } |
| |
| if (!cast<CXXRecordDecl>(record)->isPOD()) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_vecreturn_only_pod_record); |
| return; |
| } |
| |
| for (const auto *I : record->fields()) { |
| if ((count == 1) || !I->getType()->isVectorType()) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_vecreturn_only_vector_member); |
| return; |
| } |
| count++; |
| } |
| |
| D->addAttr(::new (S.Context) |
| VecReturnAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleDependencyAttr(Sema &S, Scope *Scope, Decl *D, |
| const AttributeList &Attr) { |
| 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(Attr.getLoc(), |
| diag::err_carries_dependency_param_not_function_decl); |
| return; |
| } |
| } |
| |
| D->addAttr(::new (S.Context) CarriesDependencyAttr( |
| Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleNotTailCalledAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| if (checkAttrMutualExclusion<AlwaysInlineAttr>(S, D, Attr.getRange(), |
| Attr.getName())) |
| return; |
| |
| D->addAttr(::new (S.Context) NotTailCalledAttr( |
| Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleDisableTailCallsAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| if (checkAttrMutualExclusion<NakedAttr>(S, D, Attr.getRange(), |
| Attr.getName())) |
| return; |
| |
| D->addAttr(::new (S.Context) DisableTailCallsAttr( |
| Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleUsedAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { |
| if (VD->hasLocalStorage()) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); |
| return; |
| } |
| } else if (!isFunctionOrMethod(D)) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) |
| << Attr.getName() << ExpectedVariableOrFunction; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| UsedAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleUnusedAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| bool IsCXX17Attr = Attr.isCXX11Attribute() && !Attr.getScopeName(); |
| |
| if (IsCXX17Attr && isa<VarDecl>(D)) { |
| // The C++17 spelling of this attribute cannot be applied to a static data |
| // member per [dcl.attr.unused]p2. |
| if (cast<VarDecl>(D)->isStaticDataMember()) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) |
| << Attr.getName() << ExpectedForMaybeUnused; |
| return; |
| } |
| } |
| |
| // 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(Attr.getLoc(), diag::ext_cxx17_attr) << Attr.getName(); |
| |
| D->addAttr(::new (S.Context) UnusedAttr( |
| Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleConstructorAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| uint32_t priority = ConstructorAttr::DefaultPriority; |
| if (Attr.getNumArgs() && |
| !checkUInt32Argument(S, Attr, Attr.getArgAsExpr(0), priority)) |
| return; |
| |
| D->addAttr(::new (S.Context) |
| ConstructorAttr(Attr.getRange(), S.Context, priority, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleDestructorAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| uint32_t priority = DestructorAttr::DefaultPriority; |
| if (Attr.getNumArgs() && |
| !checkUInt32Argument(S, Attr, Attr.getArgAsExpr(0), priority)) |
| return; |
| |
| D->addAttr(::new (S.Context) |
| DestructorAttr(Attr.getRange(), S.Context, priority, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| template <typename AttrTy> |
| static void handleAttrWithMessage(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| // Handle the case where the attribute has a text message. |
| StringRef Str; |
| if (Attr.getNumArgs() == 1 && !S.checkStringLiteralArgumentAttr(Attr, 0, Str)) |
| return; |
| |
| D->addAttr(::new (S.Context) AttrTy(Attr.getRange(), S.Context, Str, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleObjCSuppresProtocolAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| if (!cast<ObjCProtocolDecl>(D)->isThisDeclarationADefinition()) { |
| S.Diag(Attr.getLoc(), diag::err_objc_attr_protocol_requires_definition) |
| << Attr.getName() << Attr.getRange(); |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| ObjCExplicitProtocolImplAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| 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; |
| } |
| |
| /// \brief 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, SourceRange Range, |
| IdentifierInfo *Platform, |
| bool Implicit, |
| VersionTuple Introduced, |
| VersionTuple Deprecated, |
| VersionTuple Obsoleted, |
| bool IsUnavailable, |
| StringRef Message, |
| bool IsStrict, |
| StringRef Replacement, |
| AvailabilityMergeKind AMK, |
| unsigned AttrSpellingListIndex) { |
| 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: |
| OverrideOrImpl = true; |
| break; |
| } |
| |
| if (D->hasAttrs()) { |
| AttrVec &Attrs = D->getAttrs(); |
| for (unsigned i = 0, e = Attrs.size(); i != e;) { |
| const AvailabilityAttr *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 |
| // is explicit and the new one is implicit use the explicit one and |
| // discard the new implicit attribute. |
| if (!OldAA->isImplicit() && Implicit) { |
| return nullptr; |
| } |
| |
| // If there is an existing attribute for this platform that is implicit |
| // and the new attribute is explicit then erase the old one and |
| // continue processing the attributes. |
| if (!Implicit && OldAA->isImplicit()) { |
| 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 { |
| 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(Range.getBegin(), diag::note_overridden_method); |
| else |
| Diag(Range.getBegin(), diag::note_protocol_method); |
| } else { |
| Diag(OldAA->getLocation(), diag::warn_mismatched_availability); |
| Diag(Range.getBegin(), 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, Range, Platform, MergedIntroduced, |
| MergedDeprecated, MergedObsoleted) && |
| !OverrideOrImpl) { |
| auto *Avail = ::new (Context) AvailabilityAttr(Range, Context, Platform, |
| Introduced, Deprecated, |
| Obsoleted, IsUnavailable, Message, |
| IsStrict, Replacement, |
| AttrSpellingListIndex); |
| Avail->setImplicit(Implicit); |
| return Avail; |
| } |
| return nullptr; |
| } |
| |
| static void handleAvailabilityAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| if (!checkAttributeNumArgs(S, Attr, 1)) |
| return; |
| IdentifierLoc *Platform = Attr.getArgAsIdent(0); |
| unsigned Index = Attr.getAttributeSpellingListIndex(); |
| |
| IdentifierInfo *II = Platform->Ident; |
| if (AvailabilityAttr::getPrettyPlatformName(II->getName()).empty()) |
| S.Diag(Platform->Loc, diag::warn_availability_unknown_platform) |
| << Platform->Ident; |
| |
| NamedDecl *ND = dyn_cast<NamedDecl>(D); |
| if (!ND) // We warned about this already, so just return. |
| return; |
| |
| AvailabilityChange Introduced = Attr.getAvailabilityIntroduced(); |
| AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated(); |
| AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted(); |
| bool IsUnavailable = Attr.getUnavailableLoc().isValid(); |
| bool IsStrict = Attr.getStrictLoc().isValid(); |
| StringRef Str; |
| if (const StringLiteral *SE = |
| dyn_cast_or_null<StringLiteral>(Attr.getMessageExpr())) |
| Str = SE->getString(); |
| StringRef Replacement; |
| if (const StringLiteral *SE = |
| dyn_cast_or_null<StringLiteral>(Attr.getReplacementExpr())) |
| Replacement = SE->getString(); |
| |
| AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(ND, Attr.getRange(), II, |
| false/*Implicit*/, |
| Introduced.Version, |
| Deprecated.Version, |
| Obsoleted.Version, |
| IsUnavailable, Str, |
| IsStrict, Replacement, |
| Sema::AMK_None, |
| Index); |
| 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(2, 0); |
| }; |
| |
| auto NewIntroduced = adjustWatchOSVersion(Introduced.Version); |
| auto NewDeprecated = adjustWatchOSVersion(Deprecated.Version); |
| auto NewObsoleted = adjustWatchOSVersion(Obsoleted.Version); |
| |
| AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(ND, |
| Attr.getRange(), |
| NewII, |
| true/*Implicit*/, |
| NewIntroduced, |
| NewDeprecated, |
| NewObsoleted, |
| IsUnavailable, Str, |
| IsStrict, |
| Replacement, |
| Sema::AMK_None, |
| Index); |
| if (NewAttr) |
| D->addAttr(NewAttr); |
| } |
| } else if (S.Context.getTargetInfo().getTriple().isTvOS()) { |
| // Transcribe "ios" to "tvos" (and add a new attribute) if the versioning |
| // matches before the start of the tvOS platform. |
| IdentifierInfo *NewII = nullptr; |
| if (II->getName() == "ios") |
| NewII = &S.Context.Idents.get("tvos"); |
| else if (II->getName() == "ios_app_extension") |
| NewII = &S.Context.Idents.get("tvos_app_extension"); |
| |
| if (NewII) { |
| AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(ND, |
| Attr.getRange(), |
| NewII, |
| true/*Implicit*/, |
| Introduced.Version, |
| Deprecated.Version, |
| Obsoleted.Version, |
| IsUnavailable, Str, |
| IsStrict, |
| Replacement, |
| Sema::AMK_None, |
| Index); |
| if (NewAttr) |
| D->addAttr(NewAttr); |
| } |
| } |
| } |
| |
| 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, |
| unsigned attrSpellingListIndex) { |
| T *existingAttr = D->getAttr<T>(); |
| if (existingAttr) { |
| typename T::VisibilityType existingValue = existingAttr->getVisibility(); |
| if (existingValue == value) |
| return nullptr; |
| S.Diag(existingAttr->getLocation(), diag::err_mismatched_visibility); |
| S.Diag(range.getBegin(), diag::note_previous_attribute); |
| D->dropAttr<T>(); |
| } |
| return ::new (S.Context) T(range, S.Context, value, attrSpellingListIndex); |
| } |
| |
| VisibilityAttr *Sema::mergeVisibilityAttr(Decl *D, SourceRange Range, |
| VisibilityAttr::VisibilityType Vis, |
| unsigned AttrSpellingListIndex) { |
| return ::mergeVisibilityAttr<VisibilityAttr>(*this, D, Range, Vis, |
| AttrSpellingListIndex); |
| } |
| |
| TypeVisibilityAttr *Sema::mergeTypeVisibilityAttr(Decl *D, SourceRange Range, |
| TypeVisibilityAttr::VisibilityType Vis, |
|