| //===--- 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/Sema/SemaInternal.h" |
| #include "clang/AST/ASTContext.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/Mangle.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/Lookup.h" |
| #include "clang/Sema/Scope.h" |
| #include "llvm/ADT/StringExtras.h" |
| using namespace clang; |
| using namespace sema; |
| |
| namespace AttributeLangSupport { |
| enum LANG { |
| C, |
| Cpp, |
| ObjC |
| }; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // 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() != NULL) || isa<ObjCMethodDecl>(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)->param_begin()[Idx]->getType(); |
| } |
| |
| static QualType getFunctionOrMethodResultType(const Decl *D) { |
| if (const FunctionType *FnTy = D->getFunctionType()) |
| return cast<FunctionProtoType>(FnTy)->getReturnType(); |
| return cast<ObjCMethodDecl>(D)->getReturnType(); |
| } |
| |
| static bool isFunctionOrMethodVariadic(const Decl *D) { |
| if (const FunctionType *FnTy = D->getFunctionType()) { |
| const FunctionProtoType *proto = cast<FunctionProtoType>(FnTy); |
| return proto->isVariadic(); |
| } else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) |
| return BD->isVariadic(); |
| else { |
| 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(); |
| } |
| |
| /// \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) { |
| if (getNumAttributeArgs(Attr) != Num) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) |
| << Attr.getName() << Num; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /// \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) { |
| if (getNumAttributeArgs(Attr) < Num) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_too_few_arguments) |
| << Attr.getName() << Num; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /// \brief If Expr is a valid integer constant, get the value of the integer |
| /// expression and return success or failure. May output an error. |
| static bool checkUInt32Argument(Sema &S, const AttributeList &Attr, |
| const Expr *Expr, uint32_t &Val, |
| unsigned Idx = UINT_MAX) { |
| llvm::APSInt I(32); |
| if (Expr->isTypeDependent() || Expr->isValueDependent() || |
| !Expr->isIntegerConstantExpr(I, S.Context)) { |
| if (Idx != UINT_MAX) |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type) |
| << Attr.getName() << Idx << AANT_ArgumentIntegerConstant |
| << Expr->getSourceRange(); |
| else |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) |
| << Attr.getName() << AANT_ArgumentIntegerConstant |
| << Expr->getSourceRange(); |
| return false; |
| } |
| Val = (uint32_t)I.getZExtValue(); |
| 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, |
| const AttributeList &Attr) { |
| if (AttrTy *A = D->getAttr<AttrTy>()) { |
| S.Diag(Attr.getLoc(), diag::err_attributes_are_not_compatible) |
| << Attr.getName() << A; |
| 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. |
| static bool checkFunctionOrMethodParameterIndex(Sema &S, const Decl *D, |
| const AttributeList &Attr, |
| unsigned AttrArgNum, |
| const Expr *IdxExpr, |
| uint64_t &Idx) { |
| assert(isFunctionOrMethod(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(Attr.getLoc(), diag::err_attribute_argument_n_type) |
| << Attr.getName() << AttrArgNum << AANT_ArgumentIntegerConstant |
| << IdxExpr->getSourceRange(); |
| return false; |
| } |
| |
| Idx = IdxInt.getLimitedValue(); |
| if (Idx < 1 || (!IV && Idx > NumParams)) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds) |
| << Attr.getName() << AttrArgNum << IdxExpr->getSourceRange(); |
| return false; |
| } |
| Idx--; // Convert to zero-based. |
| if (HasImplicitThisParam) { |
| if (Idx == 0) { |
| S.Diag(Attr.getLoc(), |
| diag::err_attribute_invalid_implicit_this_argument) |
| << Attr.getName() << 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(PP.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())); |
| } |
| |
| /// \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) { |
| DeclContextLookupConstResult Res1 = RT->getDecl()->lookup( |
| S.Context.DeclarationNames.getCXXOperatorName(OO_Star)); |
| if (Res1.empty()) |
| return false; |
| |
| DeclContextLookupConstResult 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 0; |
| } |
| |
| 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 &P, |
| void *) { |
| return BS->getType()->getAs<RecordType>() |
| ->getDecl()->hasAttr<CapabilityAttr>(); |
| }, 0, 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<DeclRefExpr>(Ex)) |
| return typeHasCapability(S, E->getType()); |
| else 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) |
| 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 false; |
| } |
| |
| /// \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 |
| //===----------------------------------------------------------------------===// |
| |
| // FIXME: All this manual attribute parsing code is gross. At the |
| // least add some helper functions to check most argument patterns (# |
| // and types of args). |
| |
| 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 = 0; |
| 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 = 0; |
| 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()) { |
| const RecordType *RT = getRecordType(QT); |
| if (!RT || !RT->getDecl()->hasAttr<CapabilityAttr>()) { |
| 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 ? 0 : &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 ? 0 : &Args[0]; |
| D->addAttr(::new (S.Context) |
| AssertExclusiveLockAttr(Attr.getRange(), S.Context, |
| StartArg, Size, |
| 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 void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| Expr *Cond = Attr.getArgAsExpr(0); |
| if (!Cond->isTypeDependent()) { |
| ExprResult Converted = S.PerformContextuallyConvertToBool(Cond); |
| if (Converted.isInvalid()) |
| return; |
| Cond = Converted.take(); |
| } |
| |
| StringRef Msg; |
| if (!S.checkStringLiteralArgumentAttr(Attr, 1, Msg)) |
| return; |
| |
| SmallVector<PartialDiagnosticAt, 8> Diags; |
| if (!Cond->isValueDependent() && |
| !Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(D), |
| Diags)) { |
| S.Diag(Attr.getLoc(), diag::err_enable_if_never_constant_expr); |
| for (int I = 0, N = Diags.size(); I != N; ++I) |
| S.Diag(Diags[I].first, Diags[I].second); |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| EnableIfAttr(Attr.getRange(), S.Context, Cond, Msg, |
| 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 (!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) { |
| if (!checkAttributeNumArgs(S, Attr, 1)) return; |
| |
| 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) { |
| if (!checkAttributeNumArgs(S, Attr, 1)) return; |
| |
| 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 (!checkAttributeNumArgs(S, Attr, 1)) |
| return; |
| |
| 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 (!checkAttributeNumArgs(S, Attr, 1)) |
| return; |
| |
| 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)) { |
| // If the alignment is less than or equal to 8 bits, the packed attribute |
| // has no effect. |
| if (!FD->getType()->isDependentType() && |
| !FD->getType()->isIncompleteType() && |
| S.Context.getTypeAlign(FD->getType()) <= 8) |
| 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 |
| 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 = 0; |
| 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())); |
| } |
| |
| static void possibleTransparentUnionPointerType(QualType &T) { |
| 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()) { |
| T = QT; |
| return; |
| } |
| } |
| } |
| } |
| |
| static bool attrNonNullArgCheck(Sema &S, QualType T, const AttributeList &Attr, |
| SourceRange R, bool isReturnValue = false) { |
| T = T.getNonReferenceType(); |
| possibleTransparentUnionPointerType(T); |
| |
| if (!T->isAnyPointerType() && !T->isBlockPointerType()) { |
| S.Diag(Attr.getLoc(), |
| isReturnValue ? diag::warn_attribute_return_pointers_only |
| : diag::warn_attribute_pointers_only) |
| << Attr.getName() << R; |
| 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? |
| // FIXME: Should also highlight argument in decl in the diagnostic. |
| if (!attrNonNullArgCheck(S, getFunctionOrMethodParamType(D, Idx), Attr, |
| Ex->getSourceRange())) |
| continue; |
| |
| NonNullArgs.push_back(Idx); |
| } |
| |
| // If no arguments were specified to __attribute__((nonnull)) then all pointer |
| // arguments have a nonnull attribute. |
| if (NonNullArgs.empty()) { |
| for (unsigned i = 0, e = getFunctionOrMethodNumParams(D); i != e; ++i) { |
| QualType T = getFunctionOrMethodParamType(D, i).getNonReferenceType(); |
| possibleTransparentUnionPointerType(T); |
| if (T->isAnyPointerType() || T->isBlockPointerType()) |
| NonNullArgs.push_back(i); |
| } |
| |
| // No pointer arguments? |
| if (NonNullArgs.empty()) { |
| // Warn the trivial case only if attribute is not coming from a |
| // macro instantiation. |
| if (Attr.getLoc().isFileID()) |
| S.Diag(Attr.getLoc(), diag::warn_attribute_nonnull_no_pointers); |
| return; |
| } |
| } |
| |
| unsigned *start = &NonNullArgs[0]; |
| 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, D->getSourceRange())) |
| return; |
| |
| D->addAttr(::new (S.Context) |
| NonNullAttr(Attr.getRange(), S.Context, 0, 0, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleReturnsNonNullAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| QualType ResultType = getFunctionOrMethodResultType(D); |
| if (!attrNonNullArgCheck(S, ResultType, Attr, Attr.getRange(), |
| /* isReturnValue */ true)) |
| return; |
| |
| D->addAttr(::new (S.Context) |
| ReturnsNonNullAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| 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, 0, 0, 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; |
| |
| // Normalize the argument, __foo__ becomes foo. |
| StringRef ModuleName = Module->getName(); |
| if (ModuleName.startswith("__") && ModuleName.endswith("__") && |
| ModuleName.size() > 4) { |
| ModuleName = ModuleName.drop_front(2).drop_back(2); |
| 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>()) { |
| // FIXME: A returns attribute should conflict with any returns attribute |
| // with a different index too. |
| 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; |
| } |
| } |
| 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 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; |
| } |
| |
| // 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)) |
| 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)) |
| 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 handleKernelAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (S.LangOpts.Renderscript) { |
| D->addAttr(::new (S.Context) |
| KernelAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } else { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "kernel"; |
| } |
| } |
| |
| static void handleMallocAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { |
| QualType RetTy = FD->getReturnType(); |
| if (RetTy->isAnyPointerType() || RetTy->isBlockPointerType()) { |
| D->addAttr(::new (S.Context) |
| MallocAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| return; |
| } |
| } |
| |
| S.Diag(Attr.getLoc(), diag::warn_attribute_malloc_pointer_only); |
| } |
| |
| 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; |
| } |
| |
| D->addAttr(::new (S.Context) CommonAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &attr) { |
| if (hasDeclarator(D)) return; |
| |
| if (S.CheckNoReturnAttr(attr)) return; |
| |
| if (!isa<ObjCMethodDecl>(D)) { |
| S.Diag(attr.getLoc(), diag::warn_attribute_wrong_decl_type) |
| << attr.getName() << ExpectedFunctionOrMethod; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| NoReturnAttr(attr.getRange(), S.Context, |
| attr.getAttributeSpellingListIndex())); |
| } |
| |
| bool Sema::CheckNoReturnAttr(const AttributeList &attr) { |
| 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 (!isFunctionOrMethod(D) && !isa<BlockDecl>(D)) { |
| ValueDecl *VD = dyn_cast<ValueDecl>(D); |
| if (VD == 0 || (!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 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 handleConstructorAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| // check the attribute arguments. |
| if (Attr.getNumArgs() > 1) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) |
| << Attr.getName() << 1; |
| return; |
| } |
| |
| uint32_t priority = ConstructorAttr::DefaultPriority; |
| if (Attr.getNumArgs() > 0 && |
| !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) { |
| // check the attribute arguments. |
| if (Attr.getNumArgs() > 1) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) |
| << Attr.getName() << 1; |
| return; |
| } |
| |
| uint32_t priority = DestructorAttr::DefaultPriority; |
| if (Attr.getNumArgs() > 0 && |
| !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) { |
| unsigned NumArgs = Attr.getNumArgs(); |
| if (NumArgs > 1) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) |
| << Attr.getName() << 1; |
| return; |
| } |
| |
| // Handle the case where the attribute has a text message. |
| StringRef Str; |
| if (NumArgs == 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, |
| VersionTuple Introduced, |
| VersionTuple Deprecated, |
| VersionTuple Obsoleted, |
| bool IsUnavailable, |
| StringRef Message, |
| bool Override, |
| unsigned AttrSpellingListIndex) { |
| VersionTuple MergedIntroduced = Introduced; |
| VersionTuple MergedDeprecated = Deprecated; |
| VersionTuple MergedObsoleted = Obsoleted; |
| bool FoundAny = false; |
| |
| 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; |
| } |
| |
| FoundAny = true; |
| VersionTuple OldIntroduced = OldAA->getIntroduced(); |
| VersionTuple OldDeprecated = OldAA->getDeprecated(); |
| VersionTuple OldObsoleted = OldAA->getObsoleted(); |
| bool OldIsUnavailable = OldAA->getUnavailable(); |
| |
| if (!versionsMatch(OldIntroduced, Introduced, Override) || |
| !versionsMatch(Deprecated, OldDeprecated, Override) || |
| !versionsMatch(Obsoleted, OldObsoleted, Override) || |
| !(OldIsUnavailable == IsUnavailable || |
| (Override && !OldIsUnavailable && IsUnavailable))) { |
| if (Override) { |
| int Which = -1; |
| VersionTuple FirstVersion; |
| VersionTuple SecondVersion; |
| if (!versionsMatch(OldIntroduced, Introduced, Override)) { |
| Which = 0; |
| FirstVersion = OldIntroduced; |
| SecondVersion = Introduced; |
| } else if (!versionsMatch(Deprecated, OldDeprecated, Override)) { |
| Which = 1; |
| FirstVersion = Deprecated; |
| SecondVersion = OldDeprecated; |
| } else if (!versionsMatch(Obsoleted, OldObsoleted, Override)) { |
| Which = 2; |
| FirstVersion = Obsoleted; |
| SecondVersion = OldObsoleted; |
| } |
| |
| if (Which == -1) { |
| Diag(OldAA->getLocation(), |
| diag::warn_mismatched_availability_override_unavail) |
| << AvailabilityAttr::getPrettyPlatformName(Platform->getName()); |
| } else { |
| Diag(OldAA->getLocation(), |
| diag::warn_mismatched_availability_override) |
| << Which |
| << AvailabilityAttr::getPrettyPlatformName(Platform->getName()) |
| << FirstVersion.getAsString() << SecondVersion.getAsString(); |
| } |
| Diag(Range.getBegin(), diag::note_overridden_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 NULL; |
| |
| // Only create a new attribute if !Override, but we want to do |
| // the checking. |
| if (!checkAvailabilityAttr(*this, Range, Platform, MergedIntroduced, |
| MergedDeprecated, MergedObsoleted) && |
| !Override) { |
| return ::new (Context) AvailabilityAttr(Range, Context, Platform, |
| Introduced, Deprecated, |
| Obsoleted, IsUnavailable, Message, |
| AttrSpellingListIndex); |
| } |
| return NULL; |
| } |
| |
| 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) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); |
| return; |
| } |
| |
| AvailabilityChange Introduced = Attr.getAvailabilityIntroduced(); |
| AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated(); |
| AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted(); |
| bool IsUnavailable = Attr.getUnavailableLoc().isValid(); |
| StringRef Str; |
| if (const StringLiteral *SE = |
| dyn_cast_or_null<StringLiteral>(Attr.getMessageExpr())) |
| Str = SE->getString(); |
| |
| AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(ND, Attr.getRange(), II, |
| Introduced.Version, |
| Deprecated.Version, |
| Obsoleted.Version, |
| IsUnavailable, Str, |
| /*Override=*/false, |
| Index); |
| if (NewAttr) |
| D->addAttr(NewAttr); |
| } |
| |
| 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 NULL; |
| 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, |
| unsigned AttrSpellingListIndex) { |
| return ::mergeVisibilityAttr<TypeVisibilityAttr>(*this, D, Range, Vis, |
| AttrSpellingListIndex); |
| } |
| |
| static void handleVisibilityAttr(Sema &S, Decl *D, const AttributeList &Attr, |
| bool isTypeVisibility) { |
| // Visibility attributes don't mean anything on a typedef. |
| if (isa<TypedefNameDecl>(D)) { |
| S.Diag(Attr.getRange().getBegin(), diag::warn_attribute_ignored) |
| << Attr.getName(); |
| return; |
| } |
| |
| // 'type_visibility' can only go on a type or namespace. |
| if (isTypeVisibility && |
| !(isa<TagDecl>(D) || |
| isa<ObjCInterfaceDecl>(D) || |
| isa<NamespaceDecl>(D))) { |
| S.Diag(Attr.getRange().getBegin(), diag::err_attribute_wrong_decl_type) |
| << Attr.getName() << ExpectedTypeOrNamespace; |
| return; |
| } |
| |
| // Check that the argument is a string literal. |
| StringRef TypeStr; |
| SourceLocation LiteralLoc; |
| if (!S.checkStringLiteralArgumentAttr(Attr, 0, TypeStr, &LiteralLoc)) |
| return; |
| |
| VisibilityAttr::VisibilityType type; |
| if (!VisibilityAttr::ConvertStrToVisibilityType(TypeStr, type)) { |
| S.Diag(LiteralLoc, diag::warn_attribute_type_not_supported) |
| << Attr.getName() << TypeStr; |
| return; |
| } |
| |
| // Complain about attempts to use protected visibility on targets |
| // (like Darwin) that don't support it. |
| if (type == VisibilityAttr::Protected && |
| !S.Context.getTargetInfo().hasProtectedVisibility()) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_protected_visibility); |
| type = VisibilityAttr::Default; |
| } |
| |
| unsigned Index = Attr.getAttributeSpellingListIndex(); |
| clang::Attr *newAttr; |
| if (isTypeVisibility) { |
| newAttr = S.mergeTypeVisibilityAttr(D, Attr.getRange(), |
| (TypeVisibilityAttr::VisibilityType) type, |
| Index); |
| } else { |
| newAttr = S.mergeVisibilityAttr(D, Attr.getRange(), type, Index); |
| } |
| if (newAttr) |
| D->addAttr(newAttr); |
| } |
| |
| static void handleObjCMethodFamilyAttr(Sema &S, Decl *decl, |
| const AttributeList &Attr) { |
| ObjCMethodDecl *method = cast<ObjCMethodDecl>(decl); |
| if (!Attr.isArgIdent(0)) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type) |
| << Attr.getName() << 1 << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| IdentifierLoc *IL = Attr.getArgAsIdent(0); |
| ObjCMethodFamilyAttr::FamilyKind F; |
| if (!ObjCMethodFamilyAttr::ConvertStrToFamilyKind(IL->Ident->getName(), F)) { |
| S.Diag(IL->Loc, diag::warn_attribute_type_not_supported) << Attr.getName() |
| << IL->Ident; |
| return; |
| } |
| |
| if (F == ObjCMethodFamilyAttr::OMF_init && |
| !method->getReturnType()->isObjCObjectPointerType()) { |
| S.Diag(method->getLocation(), diag::err_init_method_bad_return_type) |
| << method->getReturnType(); |
| // Ignore the attribute. |
| return; |
| } |
| |
| method->addAttr(new (S.Context) ObjCMethodFamilyAttr(Attr.getRange(), |
| S.Context, F, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleObjCNSObject(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) { |
| QualType T = TD->getUnderlyingType(); |
| if (!T->isCARCBridgableType()) { |
| S.Diag(TD->getLocation(), diag::err_nsobject_attribute); |
| return; |
| } |
| } |
| else if (ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D)) { |
| QualType T = PD->getType(); |
| if (!T->isCARCBridgableType()) { |
| S.Diag(PD->getLocation(), diag::err_nsobject_attribute); |
| return; |
| } |
| } |
| else { |
| // It is okay to include this attribute on properties, e.g.: |
| // |
| // @property (retain, nonatomic) struct Bork *Q __attribute__((NSObject)); |
| // |
| // In this case it follows tradition and suppresses an error in the above |
| // case. |
| S.Diag(D->getLocation(), diag::warn_nsobject_attribute); |
| } |
| D->addAttr(::new (S.Context) |
| ObjCNSObjectAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleBlocksAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (!Attr.isArgIdent(0)) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type) |
| << Attr.getName() << 1 << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| IdentifierInfo *II = Attr.getArgAsIdent(0)->Ident; |
| BlocksAttr::BlockType type; |
| if (!BlocksAttr::ConvertStrToBlockType(II->getName(), type)) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_type_not_supported) |
| << Attr.getName() << II; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| BlocksAttr(Attr.getRange(), S.Context, type, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleSentinelAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| // check the attribute arguments. |
| if (Attr.getNumArgs() > 2) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) |
| << Attr.getName() << 2; |
| return; |
| } |
| |
| unsigned sentinel = (unsigned)SentinelAttr::DefaultSentinel; |
| if (Attr.getNumArgs() > 0) { |
| Expr *E = Attr.getArgAsExpr(0); |
| llvm::APSInt Idx(32); |
| if (E->isTypeDependent() || E->isValueDependent() || |
| !E->isIntegerConstantExpr(Idx, S.Context)) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type) |
| << Attr.getName() << 1 << AANT_ArgumentIntegerConstant |
| << E->getSourceRange(); |
| return; |
| } |
| |
| if (Idx.isSigned() && Idx.isNegative()) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_sentinel_less_than_zero) |
| << E->getSourceRange(); |
| return; |
| } |
| |
| sentinel = Idx.getZExtValue(); |
| } |
| |
| unsigned nullPos = (unsigned)SentinelAttr::DefaultNullPos; |
| if (Attr.getNumArgs() > 1) { |
| Expr *E = Attr.getArgAsExpr(1); |
| llvm::APSInt Idx(32); |
| if (E->isTypeDependent() || E->isValueDependent() || |
| !E->isIntegerConstantExpr(Idx, S.Context)) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type) |
| << Attr.getName() << 2 << AANT_ArgumentIntegerConstant |
| << E->getSourceRange(); |
| return; |
| } |
| nullPos = Idx.getZExtValue(); |
| |
| if ((Idx.isSigned() && Idx.isNegative()) || nullPos > 1) { |
| // FIXME: This error message could be improved, it would be nice |
| // to say what the bounds actually are. |
| S.Diag(Attr.getLoc(), diag::err_attribute_sentinel_not_zero_or_one) |
| << E->getSourceRange(); |
| return; |
| } |
| } |
| |
| if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { |
| const FunctionType *FT = FD->getType()->castAs<FunctionType>(); |
| if (isa<FunctionNoProtoType>(FT)) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_sentinel_named_arguments); |
| return; |
| } |
| |
| if (!cast<FunctionProtoType>(FT)->isVariadic()) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_sentinel_not_variadic) << 0; |
| return; |
| } |
| } else if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { |
| if (!MD->isVariadic()) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_sentinel_not_variadic) << 0; |
| return; |
| } |
| } else if (BlockDecl *BD = dyn_cast<BlockDecl>(D)) { |
| if (!BD->isVariadic()) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_sentinel_not_variadic) << 1; |
| return; |
| } |
| } else if (const VarDecl *V = dyn_cast<VarDecl>(D)) { |
| QualType Ty = V->getType(); |
| if (Ty->isBlockPointerType() || Ty->isFunctionPointerType()) { |
| const FunctionType *FT = Ty->isFunctionPointerType() |
| ? D->getFunctionType() |
| : Ty->getAs<BlockPointerType>()->getPointeeType()->getAs<FunctionType>(); |
| if (!cast<FunctionProtoType>(FT)->isVariadic()) { |
| int m = Ty->isFunctionPointerType() ? 0 : 1; |
| S.Diag(Attr.getLoc(), diag::warn_attribute_sentinel_not_variadic) << m; |
| return; |
| } |
| } else { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) |
| << Attr.getName() << ExpectedFunctionMethodOrBlock; |
| return; |
| } |
| } else { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) |
| << Attr.getName() << ExpectedFunctionMethodOrBlock; |
| return; |
| } |
| D->addAttr(::new (S.Context) |
| SentinelAttr(Attr.getRange(), S.Context, sentinel, nullPos, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleWarnUnusedResult(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (D->getFunctionType() && |
| D->getFunctionType()->getReturnType()->isVoidType()) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_void_function_method) |
| << Attr.getName() << 0; |
| return; |
| } |
| if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) |
| if (MD->getReturnType()->isVoidType()) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_void_function_method) |
| << Attr.getName() << 1; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| WarnUnusedResultAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleWeakImportAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| // weak_import only applies to variable & function declarations. |
| bool isDef = false; |
| if (!D->canBeWeakImported(isDef)) { |
| if (isDef) |
| S.Diag(Attr.getLoc(), diag::warn_attribute_invalid_on_definition) |
| << "weak_import"; |
| else if (isa<ObjCPropertyDecl>(D) || isa<ObjCMethodDecl>(D) || |
| (S.Context.getTargetInfo().getTriple().isOSDarwin() && |
| (isa<ObjCInterfaceDecl>(D) || isa<EnumDecl>(D)))) { |
| // Nothing to warn about here. |
| } else |
| S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) |
| << Attr.getName() << ExpectedVariableOrFunction; |
| |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| WeakImportAttr(Attr.getRange(), S.Context, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| // Handles reqd_work_group_size and work_group_size_hint. |
| template <typename WorkGroupAttr> |
| static void handleWorkGroupSize(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| uint32_t WGSize[3]; |
| for (unsigned i = 0; i < 3; ++i) |
| if (!checkUInt32Argument(S, Attr, Attr.getArgAsExpr(i), WGSize[i], i)) |
| return; |
| |
| WorkGroupAttr *Existing = D->getAttr<WorkGroupAttr>(); |
| if (Existing && !(Existing->getXDim() == WGSize[0] && |
| Existing->getYDim() == WGSize[1] && |
| Existing->getZDim() == WGSize[2])) |
| S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) << Attr.getName(); |
| |
| D->addAttr(::new (S.Context) WorkGroupAttr(Attr.getRange(), S.Context, |
| WGSize[0], WGSize[1], WGSize[2], |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| static void handleVecTypeHint(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (!Attr.hasParsedType()) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) |
| << Attr.getName() << 1; |
| return; |
| } |
| |
| TypeSourceInfo *ParmTSI = 0; |
| QualType ParmType = S.GetTypeFromParser(Attr.getTypeArg(), &ParmTSI); |
| assert(ParmTSI && "no type source info for attribute argument"); |
| |
| if (!ParmType->isExtVectorType() && !ParmType->isFloatingType() && |
| (ParmType->isBooleanType() || |
| !ParmType->isIntegralType(S.getASTContext()))) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_vec_type_hint) |
| << ParmType; |
| return; |
| } |
| |
| if (VecTypeHintAttr *A = D->getAttr<VecTypeHintAttr>()) { |
| if (!S.Context.hasSameType(A->getTypeHint(), ParmType)) { |
| S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) << Attr.getName(); |
| return; |
| } |
| } |
| |
| D->addAttr(::new (S.Context) VecTypeHintAttr(Attr.getLoc(), S.Context, |
| ParmTSI, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| SectionAttr *Sema::mergeSectionAttr(Decl *D, SourceRange Range, |
| StringRef Name, |
| unsigned AttrSpellingListIndex) { |
| if (SectionAttr *ExistingAttr = D->getAttr<SectionAttr>()) { |
| if (ExistingAttr->getName() == Name) |
| return NULL; |
| Diag(ExistingAttr->getLocation(), diag::warn_mismatched_section); |
| Diag(Range.getBegin(), diag::note_previous_attribute); |
| return NULL; |
| } |
| return ::new (Context) SectionAttr(Range, Context, Name, |
| AttrSpellingListIndex); |
| } |
| |
| static void handleSectionAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| // Make sure that there is a string literal as the sections's single |
| // argument. |
| StringRef Str; |
| SourceLocation LiteralLoc; |
| if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str, &LiteralLoc)) |
| return; |
| |
| // If the target wants to validate the section specifier, make it happen. |
| std::string Error = S.Context.getTargetInfo().isValidSectionSpecifier(Str); |
| if (!Error.empty()) { |
| S.Diag(LiteralLoc, diag::err_attribute_section_invalid_for_target) |
| << Error; |
| return; |
| } |
| |
| unsigned Index = Attr.getAttributeSpellingListIndex(); |
| SectionAttr *NewAttr = S.mergeSectionAttr(D, Attr.getRange(), Str, Index); |
| if (NewAttr) |
| D->addAttr(NewAttr); |
| } |
| |
| |
| static void handleCleanupAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| VarDecl *VD = cast<VarDecl>(D); |
| if (!VD->hasLocalStorage()) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); |
| return; |
| } |
| |
| Expr *E = Attr.getArgAsExpr(0); |
| SourceLocation Loc = E->getExprLoc(); |
| FunctionDecl *FD = 0; |
| DeclarationNameInfo NI; |
| |
| // gcc only allows for simple identifiers. Since we support more than gcc, we |
| // will warn the user. |
| if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { |
| if (DRE->hasQualifier()) |
| S.Diag(Loc, diag::warn_cleanup_ext); |
| FD = dyn_cast<FunctionDecl>(DRE->getDecl()); |
| NI = DRE->getNameInfo(); |
| if (!FD) { |
| S.Diag(Loc, diag::err_attribute_cleanup_arg_not_function) << 1 |
| << NI.getName(); |
| return; |
| } |
| } else if (UnresolvedLookupExpr *ULE = dyn_cast<UnresolvedLookupExpr>(E)) { |
| if (ULE->hasExplicitTemplateArgs()) |
| S.Diag(Loc, diag::warn_cleanup_ext); |
| FD = S.ResolveSingleFunctionTemplateSpecialization(ULE, true); |
| NI = ULE->getNameInfo(); |
| if (!FD) { |
| S.Diag(Loc, diag::err_attribute_cleanup_arg_not_function) << 2 |
| << NI.getName(); |
| if (ULE->getType() == S.Context.OverloadTy) |
| S.NoteAllOverloadCandidates(ULE); |
| return; |
| } |
| } else { |
| S.Diag(Loc, diag::err_attribute_cleanup_arg_not_function) << 0; |
| return; |
| } |
| |
| if (FD->getNumParams() != 1) { |
| S.Diag(Loc, diag::err_attribute_cleanup_func_must_take_one_arg) |
| << NI.getName(); |
| return; |
| } |
| |
| // We're currently more strict than GCC about what function types we accept. |
| // If this ever proves to be a problem it should be easy to fix. |
| QualType Ty = S.Context.getPointerType(VD->getType()); |
| QualType ParamTy = FD->getParamDecl(0)->getType(); |
| if (S.CheckAssignmentConstraints(FD->getParamDecl(0)->getLocation(), |
| ParamTy, Ty) != Sema::Compatible) { |
| S.Diag(Loc, diag::err_attribute_cleanup_func_arg_incompatible_type) |
| << NI.getName() << ParamTy << Ty; |
| return; |
| } |
| |
| D->addAttr(::new (S.Context) |
| CleanupAttr(Attr.getRange(), S.Context, FD, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| /// Handle __attribute__((format_arg((idx)))) attribute based on |
| /// http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html |
| static void handleFormatArgAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| Expr *IdxExpr = Attr.getArgAsExpr(0); |
| uint64_t Idx; |
| if (!checkFunctionOrMethodParameterIndex(S, D, Attr, 1, IdxExpr, Idx)) |
| return; |
| |
| // make sure the format string is really a string |
| QualType Ty = getFunctionOrMethodParamType(D, Idx); |
| |
| bool not_nsstring_type = !isNSStringType(Ty, S.Context); |
| if (not_nsstring_type && |
| !isCFStringType(Ty, S.Context) && |
| (!Ty->isPointerType() || |
| !Ty->getAs<PointerType>()->getPointeeType()->isCharType())) { |
| // FIXME: Should highlight the actual expression that has the wrong type. |
| S.Diag(Attr.getLoc(), diag::err_format_attribute_not) |
| << (not_nsstring_type ? "a string type" : "an NSString") |
| << IdxExpr->getSourceRange(); |
| return; |
| } |
| Ty = getFunctionOrMethodResultType(D); |
| if (!isNSStringType(Ty, S.Context) && |
| !isCFStringType(Ty, S.Context) && |
| (!Ty->isPointerType() || |
| !Ty->getAs<PointerType>()->getPointeeType()->isCharType())) { |
| // FIXME: Should highlight the actual expression that has the wrong type. |
| S.Diag(Attr.getLoc(), diag::err_format_attribute_result_not) |
| << (not_nsstring_type ? "string type" : "NSString") |
| << IdxExpr->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; |
| IdxExpr->EvaluateAsInt(Val, S.Context); |
| |
| D->addAttr(::new (S.Context) |
| FormatArgAttr(Attr.getRange(), S.Context, Val.getZExtValue(), |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| enum FormatAttrKind { |
| CFStringFormat, |
| NSStringFormat, |
| StrftimeFormat, |
| SupportedFormat, |
| IgnoredFormat, |
| InvalidFormat |
| }; |
| |
| /// getFormatAttrKind - Map from format attribute names to supported format |
| /// types. |
| static FormatAttrKind getFormatAttrKind(StringRef Format) { |
| return llvm::StringSwitch<FormatAttrKind>(Format) |
| // Check for formats that get handled specially. |
| .Case("NSString", NSStringFormat) |
| .Case("CFString", CFStringFormat) |
| .Case("strftime", StrftimeFormat) |
| |
| // Otherwise, check for supported formats. |
| .Cases("scanf", "printf", "printf0", "strfmon", SupportedFormat) |
| .Cases("cmn_err", "vcmn_err", "zcmn_err", SupportedFormat) |
| .Case("kprintf", SupportedFormat) // OpenBSD. |
| |
| .Cases("gcc_diag", "gcc_cdiag", "gcc_cxxdiag", "gcc_tdiag", IgnoredFormat) |
| .Default(InvalidFormat); |
| } |
| |
| /// Handle __attribute__((init_priority(priority))) attributes based on |
| /// http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html |
| static void handleInitPriorityAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| if (!S.getLangOpts().CPlusPlus) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); |
| return; |
| } |
| |
| if (S.getCurFunctionOrMethodDecl()) { |
| S.Diag(Attr.getLoc(), diag::err_init_priority_object_attr); |
| Attr.setInvalid(); |
| return; |
| } |
| QualType T = cast<VarDecl>(D)->getType(); |
| if (S.Context.getAsArrayType(T)) |
| T = S.Context.getBaseElementType(T); |
| if (!T->getAs<RecordType>()) { |
| S.Diag(Attr.getLoc(), diag::err_init_priority_object_attr); |
| Attr.setInvalid(); |
| return; |
| } |
| |
| Expr *E = Attr.getArgAsExpr(0); |
| uint32_t prioritynum; |
| if (!checkUInt32Argument(S, Attr, E, prioritynum)) { |
| Attr.setInvalid(); |
| return; |
| } |
| |
| if (prioritynum < 101 || prioritynum > 65535) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_outof_range) |
| << E->getSourceRange(); |
| Attr.setInvalid(); |
| return; |
| } |
| D->addAttr(::new (S.Context) |
| InitPriorityAttr(Attr.getRange(), S.Context, prioritynum, |
| Attr.getAttributeSpellingListIndex())); |
| } |
| |
| FormatAttr *Sema::mergeFormatAttr(Decl *D, SourceRange Range, |
| IdentifierInfo *Format, int FormatIdx, |
| int FirstArg, |
| unsigned AttrSpellingListIndex) { |
| // Check whether we already have an equivalent format attribute. |
| for (auto *F : D->specific_attrs<FormatAttr>()) { |
| if (F->getType() == Format && |
| F->getFormatIdx() == FormatIdx && |
| F->getFirstArg() == FirstArg) { |
| // If we don't have a valid location for this attribute, adopt the |
| // location. |
| if (F->getLocation().isInvalid()) |
| F->setRange(Range); |
| return NULL; |
| } |
| } |
| |
| return ::new (Context) FormatAttr(Range, Context, Format, FormatIdx, |
| FirstArg, AttrSpellingListIndex); |
| } |
| |
| /// Handle __attribute__((format(type,idx,firstarg))) attributes based on |
| /// http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html |
| static void handleFormatAttr(Sema &S, Decl *D, const AttributeList &Attr) { |
| if (!Attr.isArgIdent(0)) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type) |
| << Attr.getName() << 1 << AANT_ArgumentIdentifier; |
| return; |
| } |
| |
| // In C++ the implicit 'this' function parameter also counts, and they are |
| // counted from one. |
| bool HasImplicitThisParam = isInstanceMethod(D); |
| unsigned NumArgs = getFunctionOrMethodNumParams(D) + HasImplicitThisParam; |
| |
| IdentifierInfo *II = Attr.getArgAsIdent(0)->Ident; |
| StringRef Format = II->getName(); |
| |
| // Normalize the argument, __foo__ becomes foo. |
| if (Format.startswith("__") && Format.endswith("__")) { |
| Format = Format.substr(2, Format.size() - 4); |
| // If we've modified the string name, we need a new identifier for it. |
| II = &S.Context.Idents.get(Format); |
| } |
| |
| // Check for supported formats. |
| FormatAttrKind Kind = getFormatAttrKind(Format); |
| |
| if (Kind == IgnoredFormat) |
| return; |
| |
| if (Kind == InvalidFormat) { |
| S.Diag(Attr.getLoc(), diag::warn_attribute_type_not_supported) |
| << Attr.getName() << II->getName(); |
| return; |
| } |
| |
| // checks for the 2nd argument |
| Expr *IdxExpr = Attr.getArgAsExpr(1); |
| uint32_t Idx; |
| if (!checkUInt32Argument(S, Attr, IdxExpr, Idx, 2)) |
| return; |
| |
| if (Idx < 1 || Idx > NumArgs) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds) |
| << Attr.getName() << 2 << IdxExpr->getSourceRange(); |
| return; |
| } |
| |
| // FIXME: Do we need to bounds check? |
| unsigned ArgIdx = Idx - 1; |
| |
| if (HasImplicitThisParam) { |
| if (ArgIdx == 0) { |
| S.Diag(Attr.getLoc(), |
| diag::err_format_attribute_implicit_this_format_string) |
| << IdxExpr->getSourceRange(); |
| return; |
| } |
| ArgIdx--; |
| } |
| |
| // make sure the format string is really a string |
| QualType Ty = getFunctionOrMethodParamType(D, ArgIdx); |
| |
| if (Kind == CFStringFormat) { |
| if (!isCFStringType(Ty, S.Context)) { |
| S.Diag(Attr.getLoc(), diag::err_format_attribute_not) |
| << "a CFString" << IdxExpr->getSourceRange(); |
| return; |
| } |
| } else if (Kind == NSStringFormat) { |
| // FIXME: do we need to check if the type is NSString*? What are the |
| // semantics? |
| if (!isNSStringType(Ty, S.Context)) { |
| // FIXME: Should highlight the actual expression that has the wrong type. |
| S.Diag(Attr.getLoc(), diag::err_format_attribute_not) |
| << "an NSString" << IdxExpr->getSourceRange(); |
| return; |
| } |
| } else if (!Ty->isPointerType() || |
| !Ty->getAs<PointerType>()->getPointeeType()->isCharType()) { |
| // FIXME: Should highlight the actual expression that has the wrong type. |
| S.Diag(Attr.getLoc(), diag::err_format_attribute_not) |
| << "a string type" << IdxExpr->getSourceRange(); |
| return; |
| } |
| |
| // check the 3rd argument |
| Expr *FirstArgExpr = Attr.getArgAsExpr(2); |
| uint32_t FirstArg; |
| if (!checkUInt32Argument(S, Attr, FirstArgExpr, FirstArg, 3)) |
| return; |
| |
| // check if the function is variadic if the 3rd argument non-zero |
| if (FirstArg != 0) { |
| if (isFunctionOrMethodVariadic(D)) { |
| ++NumArgs; // +1 for ... |
| } else { |
| S.Diag(D->getLocation(), diag::err_format_attribute_requires_variadic); |
| return; |
| } |
| } |
| |
| // strftime requires FirstArg to be 0 because it doesn't read from any |
| // variable the input is just the current time + the format string. |
| if (Kind == StrftimeFormat) { |
| if (FirstArg != 0) { |
| S.Diag(Attr.getLoc(), diag::err_format_strftime_third_parameter) |
| << FirstArgExpr->getSourceRange(); |
| return; |
| } |
| // if 0 it disables parameter checking (to use with e.g. va_list) |
| } else if (FirstArg != 0 && FirstArg != NumArgs) { |
| S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds) |
| << Attr.getName() << 3 << FirstArgExpr->getSourceRange(); |
| return; |
| } |
| |
| FormatAttr *NewAttr = S.mergeFormatAttr(D, Attr.getRange(), II, |
| Idx, FirstArg, |
| Attr.getAttributeSpellingListIndex()); |
| if (NewAttr) |
| D->addAttr(NewAttr); |
| } |
| |
| static void handleTransparentUnionAttr(Sema &S, Decl *D, |
| const AttributeList &Attr) { |
| // Try to find the underlying union declaration. |
| RecordDecl *RD = 0; |
| TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D); |
| if ( |