diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Sema/SemaObjC.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Sema/SemaObjC.cpp | 2408 |
1 files changed, 2408 insertions, 0 deletions
diff --git a/contrib/llvm-project/clang/lib/Sema/SemaObjC.cpp b/contrib/llvm-project/clang/lib/Sema/SemaObjC.cpp new file mode 100644 index 000000000000..75233689769c --- /dev/null +++ b/contrib/llvm-project/clang/lib/Sema/SemaObjC.cpp @@ -0,0 +1,2408 @@ +//===----- SemaObjC.cpp ---- Semantic Analysis for Objective-C ------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements semantic analysis for Objective-C. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Sema/SemaObjC.h" +#include "clang/AST/ASTMutationListener.h" +#include "clang/AST/EvaluatedExprVisitor.h" +#include "clang/AST/StmtObjC.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/Attr.h" +#include "clang/Sema/ParsedAttr.h" +#include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/TemplateDeduction.h" +#include "llvm/Support/ConvertUTF.h" + +namespace clang { + +SemaObjC::SemaObjC(Sema &S) + : SemaBase(S), NSNumberDecl(nullptr), NSValueDecl(nullptr), + NSStringDecl(nullptr), StringWithUTF8StringMethod(nullptr), + ValueWithBytesObjCTypeMethod(nullptr), NSArrayDecl(nullptr), + ArrayWithObjectsMethod(nullptr), NSDictionaryDecl(nullptr), + DictionaryWithObjectsMethod(nullptr) {} + +StmtResult SemaObjC::ActOnObjCForCollectionStmt(SourceLocation ForLoc, + Stmt *First, Expr *collection, + SourceLocation RParenLoc) { + ASTContext &Context = getASTContext(); + SemaRef.setFunctionHasBranchProtectedScope(); + + ExprResult CollectionExprResult = + CheckObjCForCollectionOperand(ForLoc, collection); + + if (First) { + QualType FirstType; + if (DeclStmt *DS = dyn_cast<DeclStmt>(First)) { + if (!DS->isSingleDecl()) + return StmtError(Diag((*DS->decl_begin())->getLocation(), + diag::err_toomany_element_decls)); + + VarDecl *D = dyn_cast<VarDecl>(DS->getSingleDecl()); + if (!D || D->isInvalidDecl()) + return StmtError(); + + FirstType = D->getType(); + // C99 6.8.5p3: The declaration part of a 'for' statement shall only + // declare identifiers for objects having storage class 'auto' or + // 'register'. + if (!D->hasLocalStorage()) + return StmtError( + Diag(D->getLocation(), diag::err_non_local_variable_decl_in_for)); + + // If the type contained 'auto', deduce the 'auto' to 'id'. + if (FirstType->getContainedAutoType()) { + SourceLocation Loc = D->getLocation(); + OpaqueValueExpr OpaqueId(Loc, Context.getObjCIdType(), VK_PRValue); + Expr *DeducedInit = &OpaqueId; + sema::TemplateDeductionInfo Info(Loc); + FirstType = QualType(); + TemplateDeductionResult Result = SemaRef.DeduceAutoType( + D->getTypeSourceInfo()->getTypeLoc(), DeducedInit, FirstType, Info); + if (Result != TemplateDeductionResult::Success && + Result != TemplateDeductionResult::AlreadyDiagnosed) + SemaRef.DiagnoseAutoDeductionFailure(D, DeducedInit); + if (FirstType.isNull()) { + D->setInvalidDecl(); + return StmtError(); + } + + D->setType(FirstType); + + if (!SemaRef.inTemplateInstantiation()) { + SourceLocation Loc = + D->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); + Diag(Loc, diag::warn_auto_var_is_id) << D->getDeclName(); + } + } + + } else { + Expr *FirstE = cast<Expr>(First); + if (!FirstE->isTypeDependent() && !FirstE->isLValue()) + return StmtError( + Diag(First->getBeginLoc(), diag::err_selector_element_not_lvalue) + << First->getSourceRange()); + + FirstType = static_cast<Expr *>(First)->getType(); + if (FirstType.isConstQualified()) + Diag(ForLoc, diag::err_selector_element_const_type) + << FirstType << First->getSourceRange(); + } + if (!FirstType->isDependentType() && + !FirstType->isObjCObjectPointerType() && + !FirstType->isBlockPointerType()) + return StmtError(Diag(ForLoc, diag::err_selector_element_type) + << FirstType << First->getSourceRange()); + } + + if (CollectionExprResult.isInvalid()) + return StmtError(); + + CollectionExprResult = SemaRef.ActOnFinishFullExpr(CollectionExprResult.get(), + /*DiscardedValue*/ false); + if (CollectionExprResult.isInvalid()) + return StmtError(); + + return new (Context) ObjCForCollectionStmt(First, CollectionExprResult.get(), + nullptr, ForLoc, RParenLoc); +} + +ExprResult SemaObjC::CheckObjCForCollectionOperand(SourceLocation forLoc, + Expr *collection) { + ASTContext &Context = getASTContext(); + if (!collection) + return ExprError(); + + ExprResult result = SemaRef.CorrectDelayedTyposInExpr(collection); + if (!result.isUsable()) + return ExprError(); + collection = result.get(); + + // Bail out early if we've got a type-dependent expression. + if (collection->isTypeDependent()) + return collection; + + // Perform normal l-value conversion. + result = SemaRef.DefaultFunctionArrayLvalueConversion(collection); + if (result.isInvalid()) + return ExprError(); + collection = result.get(); + + // The operand needs to have object-pointer type. + // TODO: should we do a contextual conversion? + const ObjCObjectPointerType *pointerType = + collection->getType()->getAs<ObjCObjectPointerType>(); + if (!pointerType) + return Diag(forLoc, diag::err_collection_expr_type) + << collection->getType() << collection->getSourceRange(); + + // Check that the operand provides + // - countByEnumeratingWithState:objects:count: + const ObjCObjectType *objectType = pointerType->getObjectType(); + ObjCInterfaceDecl *iface = objectType->getInterface(); + + // If we have a forward-declared type, we can't do this check. + // Under ARC, it is an error not to have a forward-declared class. + if (iface && + (getLangOpts().ObjCAutoRefCount + ? SemaRef.RequireCompleteType(forLoc, QualType(objectType, 0), + diag::err_arc_collection_forward, + collection) + : !SemaRef.isCompleteType(forLoc, QualType(objectType, 0)))) { + // Otherwise, if we have any useful type information, check that + // the type declares the appropriate method. + } else if (iface || !objectType->qual_empty()) { + const IdentifierInfo *selectorIdents[] = { + &Context.Idents.get("countByEnumeratingWithState"), + &Context.Idents.get("objects"), &Context.Idents.get("count")}; + Selector selector = Context.Selectors.getSelector(3, &selectorIdents[0]); + + ObjCMethodDecl *method = nullptr; + + // If there's an interface, look in both the public and private APIs. + if (iface) { + method = iface->lookupInstanceMethod(selector); + if (!method) + method = iface->lookupPrivateMethod(selector); + } + + // Also check protocol qualifiers. + if (!method) + method = LookupMethodInQualifiedType(selector, pointerType, + /*instance*/ true); + + // If we didn't find it anywhere, give up. + if (!method) { + Diag(forLoc, diag::warn_collection_expr_type) + << collection->getType() << selector << collection->getSourceRange(); + } + + // TODO: check for an incompatible signature? + } + + // Wrap up any cleanups in the expression. + return collection; +} + +StmtResult SemaObjC::FinishObjCForCollectionStmt(Stmt *S, Stmt *B) { + if (!S || !B) + return StmtError(); + ObjCForCollectionStmt *ForStmt = cast<ObjCForCollectionStmt>(S); + + ForStmt->setBody(B); + return S; +} + +StmtResult SemaObjC::ActOnObjCAtCatchStmt(SourceLocation AtLoc, + SourceLocation RParen, Decl *Parm, + Stmt *Body) { + ASTContext &Context = getASTContext(); + VarDecl *Var = cast_or_null<VarDecl>(Parm); + if (Var && Var->isInvalidDecl()) + return StmtError(); + + return new (Context) ObjCAtCatchStmt(AtLoc, RParen, Var, Body); +} + +StmtResult SemaObjC::ActOnObjCAtFinallyStmt(SourceLocation AtLoc, Stmt *Body) { + ASTContext &Context = getASTContext(); + return new (Context) ObjCAtFinallyStmt(AtLoc, Body); +} + +StmtResult SemaObjC::ActOnObjCAtTryStmt(SourceLocation AtLoc, Stmt *Try, + MultiStmtArg CatchStmts, + Stmt *Finally) { + ASTContext &Context = getASTContext(); + if (!getLangOpts().ObjCExceptions) + Diag(AtLoc, diag::err_objc_exceptions_disabled) << "@try"; + + // Objective-C try is incompatible with SEH __try. + sema::FunctionScopeInfo *FSI = SemaRef.getCurFunction(); + if (FSI->FirstSEHTryLoc.isValid()) { + Diag(AtLoc, diag::err_mixing_cxx_try_seh_try) << 1; + Diag(FSI->FirstSEHTryLoc, diag::note_conflicting_try_here) << "'__try'"; + } + + FSI->setHasObjCTry(AtLoc); + unsigned NumCatchStmts = CatchStmts.size(); + return ObjCAtTryStmt::Create(Context, AtLoc, Try, CatchStmts.data(), + NumCatchStmts, Finally); +} + +StmtResult SemaObjC::BuildObjCAtThrowStmt(SourceLocation AtLoc, Expr *Throw) { + ASTContext &Context = getASTContext(); + if (Throw) { + ExprResult Result = SemaRef.DefaultLvalueConversion(Throw); + if (Result.isInvalid()) + return StmtError(); + + Result = + SemaRef.ActOnFinishFullExpr(Result.get(), /*DiscardedValue*/ false); + if (Result.isInvalid()) + return StmtError(); + Throw = Result.get(); + + QualType ThrowType = Throw->getType(); + // Make sure the expression type is an ObjC pointer or "void *". + if (!ThrowType->isDependentType() && + !ThrowType->isObjCObjectPointerType()) { + const PointerType *PT = ThrowType->getAs<PointerType>(); + if (!PT || !PT->getPointeeType()->isVoidType()) + return StmtError(Diag(AtLoc, diag::err_objc_throw_expects_object) + << Throw->getType() << Throw->getSourceRange()); + } + } + + return new (Context) ObjCAtThrowStmt(AtLoc, Throw); +} + +StmtResult SemaObjC::ActOnObjCAtThrowStmt(SourceLocation AtLoc, Expr *Throw, + Scope *CurScope) { + if (!getLangOpts().ObjCExceptions) + Diag(AtLoc, diag::err_objc_exceptions_disabled) << "@throw"; + + if (!Throw) { + // @throw without an expression designates a rethrow (which must occur + // in the context of an @catch clause). + Scope *AtCatchParent = CurScope; + while (AtCatchParent && !AtCatchParent->isAtCatchScope()) + AtCatchParent = AtCatchParent->getParent(); + if (!AtCatchParent) + return StmtError(Diag(AtLoc, diag::err_rethrow_used_outside_catch)); + } + return BuildObjCAtThrowStmt(AtLoc, Throw); +} + +ExprResult SemaObjC::ActOnObjCAtSynchronizedOperand(SourceLocation atLoc, + Expr *operand) { + ExprResult result = SemaRef.DefaultLvalueConversion(operand); + if (result.isInvalid()) + return ExprError(); + operand = result.get(); + + // Make sure the expression type is an ObjC pointer or "void *". + QualType type = operand->getType(); + if (!type->isDependentType() && !type->isObjCObjectPointerType()) { + const PointerType *pointerType = type->getAs<PointerType>(); + if (!pointerType || !pointerType->getPointeeType()->isVoidType()) { + if (getLangOpts().CPlusPlus) { + if (SemaRef.RequireCompleteType(atLoc, type, + diag::err_incomplete_receiver_type)) + return Diag(atLoc, diag::err_objc_synchronized_expects_object) + << type << operand->getSourceRange(); + + ExprResult result = + SemaRef.PerformContextuallyConvertToObjCPointer(operand); + if (result.isInvalid()) + return ExprError(); + if (!result.isUsable()) + return Diag(atLoc, diag::err_objc_synchronized_expects_object) + << type << operand->getSourceRange(); + + operand = result.get(); + } else { + return Diag(atLoc, diag::err_objc_synchronized_expects_object) + << type << operand->getSourceRange(); + } + } + } + + // The operand to @synchronized is a full-expression. + return SemaRef.ActOnFinishFullExpr(operand, /*DiscardedValue*/ false); +} + +StmtResult SemaObjC::ActOnObjCAtSynchronizedStmt(SourceLocation AtLoc, + Expr *SyncExpr, + Stmt *SyncBody) { + ASTContext &Context = getASTContext(); + // We can't jump into or indirect-jump out of a @synchronized block. + SemaRef.setFunctionHasBranchProtectedScope(); + return new (Context) ObjCAtSynchronizedStmt(AtLoc, SyncExpr, SyncBody); +} + +StmtResult SemaObjC::ActOnObjCAutoreleasePoolStmt(SourceLocation AtLoc, + Stmt *Body) { + ASTContext &Context = getASTContext(); + SemaRef.setFunctionHasBranchProtectedScope(); + return new (Context) ObjCAutoreleasePoolStmt(AtLoc, Body); +} + +TypeResult SemaObjC::actOnObjCProtocolQualifierType( + SourceLocation lAngleLoc, ArrayRef<Decl *> protocols, + ArrayRef<SourceLocation> protocolLocs, SourceLocation rAngleLoc) { + ASTContext &Context = getASTContext(); + // Form id<protocol-list>. + QualType Result = Context.getObjCObjectType( + Context.ObjCBuiltinIdTy, {}, + llvm::ArrayRef((ObjCProtocolDecl *const *)protocols.data(), + protocols.size()), + false); + Result = Context.getObjCObjectPointerType(Result); + + TypeSourceInfo *ResultTInfo = Context.CreateTypeSourceInfo(Result); + TypeLoc ResultTL = ResultTInfo->getTypeLoc(); + + auto ObjCObjectPointerTL = ResultTL.castAs<ObjCObjectPointerTypeLoc>(); + ObjCObjectPointerTL.setStarLoc(SourceLocation()); // implicit + + auto ObjCObjectTL = + ObjCObjectPointerTL.getPointeeLoc().castAs<ObjCObjectTypeLoc>(); + ObjCObjectTL.setHasBaseTypeAsWritten(false); + ObjCObjectTL.getBaseLoc().initialize(Context, SourceLocation()); + + // No type arguments. + ObjCObjectTL.setTypeArgsLAngleLoc(SourceLocation()); + ObjCObjectTL.setTypeArgsRAngleLoc(SourceLocation()); + + // Fill in protocol qualifiers. + ObjCObjectTL.setProtocolLAngleLoc(lAngleLoc); + ObjCObjectTL.setProtocolRAngleLoc(rAngleLoc); + for (unsigned i = 0, n = protocols.size(); i != n; ++i) + ObjCObjectTL.setProtocolLoc(i, protocolLocs[i]); + + // We're done. Return the completed type to the parser. + return SemaRef.CreateParsedType(Result, ResultTInfo); +} + +TypeResult SemaObjC::actOnObjCTypeArgsAndProtocolQualifiers( + Scope *S, SourceLocation Loc, ParsedType BaseType, + SourceLocation TypeArgsLAngleLoc, ArrayRef<ParsedType> TypeArgs, + SourceLocation TypeArgsRAngleLoc, SourceLocation ProtocolLAngleLoc, + ArrayRef<Decl *> Protocols, ArrayRef<SourceLocation> ProtocolLocs, + SourceLocation ProtocolRAngleLoc) { + ASTContext &Context = getASTContext(); + TypeSourceInfo *BaseTypeInfo = nullptr; + QualType T = SemaRef.GetTypeFromParser(BaseType, &BaseTypeInfo); + if (T.isNull()) + return true; + + // Handle missing type-source info. + if (!BaseTypeInfo) + BaseTypeInfo = Context.getTrivialTypeSourceInfo(T, Loc); + + // Extract type arguments. + SmallVector<TypeSourceInfo *, 4> ActualTypeArgInfos; + for (unsigned i = 0, n = TypeArgs.size(); i != n; ++i) { + TypeSourceInfo *TypeArgInfo = nullptr; + QualType TypeArg = SemaRef.GetTypeFromParser(TypeArgs[i], &TypeArgInfo); + if (TypeArg.isNull()) { + ActualTypeArgInfos.clear(); + break; + } + + assert(TypeArgInfo && "No type source info?"); + ActualTypeArgInfos.push_back(TypeArgInfo); + } + + // Build the object type. + QualType Result = BuildObjCObjectType( + T, BaseTypeInfo->getTypeLoc().getSourceRange().getBegin(), + TypeArgsLAngleLoc, ActualTypeArgInfos, TypeArgsRAngleLoc, + ProtocolLAngleLoc, + llvm::ArrayRef((ObjCProtocolDecl *const *)Protocols.data(), + Protocols.size()), + ProtocolLocs, ProtocolRAngleLoc, + /*FailOnError=*/false, + /*Rebuilding=*/false); + + if (Result == T) + return BaseType; + + // Create source information for this type. + TypeSourceInfo *ResultTInfo = Context.CreateTypeSourceInfo(Result); + TypeLoc ResultTL = ResultTInfo->getTypeLoc(); + + // For id<Proto1, Proto2> or Class<Proto1, Proto2>, we'll have an + // object pointer type. Fill in source information for it. + if (auto ObjCObjectPointerTL = ResultTL.getAs<ObjCObjectPointerTypeLoc>()) { + // The '*' is implicit. + ObjCObjectPointerTL.setStarLoc(SourceLocation()); + ResultTL = ObjCObjectPointerTL.getPointeeLoc(); + } + + if (auto OTPTL = ResultTL.getAs<ObjCTypeParamTypeLoc>()) { + // Protocol qualifier information. + if (OTPTL.getNumProtocols() > 0) { + assert(OTPTL.getNumProtocols() == Protocols.size()); + OTPTL.setProtocolLAngleLoc(ProtocolLAngleLoc); + OTPTL.setProtocolRAngleLoc(ProtocolRAngleLoc); + for (unsigned i = 0, n = Protocols.size(); i != n; ++i) + OTPTL.setProtocolLoc(i, ProtocolLocs[i]); + } + + // We're done. Return the completed type to the parser. + return SemaRef.CreateParsedType(Result, ResultTInfo); + } + + auto ObjCObjectTL = ResultTL.castAs<ObjCObjectTypeLoc>(); + + // Type argument information. + if (ObjCObjectTL.getNumTypeArgs() > 0) { + assert(ObjCObjectTL.getNumTypeArgs() == ActualTypeArgInfos.size()); + ObjCObjectTL.setTypeArgsLAngleLoc(TypeArgsLAngleLoc); + ObjCObjectTL.setTypeArgsRAngleLoc(TypeArgsRAngleLoc); + for (unsigned i = 0, n = ActualTypeArgInfos.size(); i != n; ++i) + ObjCObjectTL.setTypeArgTInfo(i, ActualTypeArgInfos[i]); + } else { + ObjCObjectTL.setTypeArgsLAngleLoc(SourceLocation()); + ObjCObjectTL.setTypeArgsRAngleLoc(SourceLocation()); + } + + // Protocol qualifier information. + if (ObjCObjectTL.getNumProtocols() > 0) { + assert(ObjCObjectTL.getNumProtocols() == Protocols.size()); + ObjCObjectTL.setProtocolLAngleLoc(ProtocolLAngleLoc); + ObjCObjectTL.setProtocolRAngleLoc(ProtocolRAngleLoc); + for (unsigned i = 0, n = Protocols.size(); i != n; ++i) + ObjCObjectTL.setProtocolLoc(i, ProtocolLocs[i]); + } else { + ObjCObjectTL.setProtocolLAngleLoc(SourceLocation()); + ObjCObjectTL.setProtocolRAngleLoc(SourceLocation()); + } + + // Base type. + ObjCObjectTL.setHasBaseTypeAsWritten(true); + if (ObjCObjectTL.getType() == T) + ObjCObjectTL.getBaseLoc().initializeFullCopy(BaseTypeInfo->getTypeLoc()); + else + ObjCObjectTL.getBaseLoc().initialize(Context, Loc); + + // We're done. Return the completed type to the parser. + return SemaRef.CreateParsedType(Result, ResultTInfo); +} + +QualType SemaObjC::BuildObjCTypeParamType( + const ObjCTypeParamDecl *Decl, SourceLocation ProtocolLAngleLoc, + ArrayRef<ObjCProtocolDecl *> Protocols, + ArrayRef<SourceLocation> ProtocolLocs, SourceLocation ProtocolRAngleLoc, + bool FailOnError) { + ASTContext &Context = getASTContext(); + QualType Result = QualType(Decl->getTypeForDecl(), 0); + if (!Protocols.empty()) { + bool HasError; + Result = Context.applyObjCProtocolQualifiers(Result, Protocols, HasError); + if (HasError) { + Diag(SourceLocation(), diag::err_invalid_protocol_qualifiers) + << SourceRange(ProtocolLAngleLoc, ProtocolRAngleLoc); + if (FailOnError) + Result = QualType(); + } + if (FailOnError && Result.isNull()) + return QualType(); + } + + return Result; +} + +/// Apply Objective-C type arguments to the given type. +static QualType applyObjCTypeArgs(Sema &S, SourceLocation loc, QualType type, + ArrayRef<TypeSourceInfo *> typeArgs, + SourceRange typeArgsRange, bool failOnError, + bool rebuilding) { + // We can only apply type arguments to an Objective-C class type. + const auto *objcObjectType = type->getAs<ObjCObjectType>(); + if (!objcObjectType || !objcObjectType->getInterface()) { + S.Diag(loc, diag::err_objc_type_args_non_class) << type << typeArgsRange; + + if (failOnError) + return QualType(); + return type; + } + + // The class type must be parameterized. + ObjCInterfaceDecl *objcClass = objcObjectType->getInterface(); + ObjCTypeParamList *typeParams = objcClass->getTypeParamList(); + if (!typeParams) { + S.Diag(loc, diag::err_objc_type_args_non_parameterized_class) + << objcClass->getDeclName() << FixItHint::CreateRemoval(typeArgsRange); + + if (failOnError) + return QualType(); + + return type; + } + + // The type must not already be specialized. + if (objcObjectType->isSpecialized()) { + S.Diag(loc, diag::err_objc_type_args_specialized_class) + << type << FixItHint::CreateRemoval(typeArgsRange); + + if (failOnError) + return QualType(); + + return type; + } + + // Check the type arguments. + SmallVector<QualType, 4> finalTypeArgs; + unsigned numTypeParams = typeParams->size(); + bool anyPackExpansions = false; + for (unsigned i = 0, n = typeArgs.size(); i != n; ++i) { + TypeSourceInfo *typeArgInfo = typeArgs[i]; + QualType typeArg = typeArgInfo->getType(); + + // Type arguments cannot have explicit qualifiers or nullability. + // We ignore indirect sources of these, e.g. behind typedefs or + // template arguments. + if (TypeLoc qual = typeArgInfo->getTypeLoc().findExplicitQualifierLoc()) { + bool diagnosed = false; + SourceRange rangeToRemove; + if (auto attr = qual.getAs<AttributedTypeLoc>()) { + rangeToRemove = attr.getLocalSourceRange(); + if (attr.getTypePtr()->getImmediateNullability()) { + typeArg = attr.getTypePtr()->getModifiedType(); + S.Diag(attr.getBeginLoc(), + diag::err_objc_type_arg_explicit_nullability) + << typeArg << FixItHint::CreateRemoval(rangeToRemove); + diagnosed = true; + } + } + + // When rebuilding, qualifiers might have gotten here through a + // final substitution. + if (!rebuilding && !diagnosed) { + S.Diag(qual.getBeginLoc(), diag::err_objc_type_arg_qualified) + << typeArg << typeArg.getQualifiers().getAsString() + << FixItHint::CreateRemoval(rangeToRemove); + } + } + + // Remove qualifiers even if they're non-local. + typeArg = typeArg.getUnqualifiedType(); + + finalTypeArgs.push_back(typeArg); + + if (typeArg->getAs<PackExpansionType>()) + anyPackExpansions = true; + + // Find the corresponding type parameter, if there is one. + ObjCTypeParamDecl *typeParam = nullptr; + if (!anyPackExpansions) { + if (i < numTypeParams) { + typeParam = typeParams->begin()[i]; + } else { + // Too many arguments. + S.Diag(loc, diag::err_objc_type_args_wrong_arity) + << false << objcClass->getDeclName() << (unsigned)typeArgs.size() + << numTypeParams; + S.Diag(objcClass->getLocation(), diag::note_previous_decl) << objcClass; + + if (failOnError) + return QualType(); + + return type; + } + } + + // Objective-C object pointer types must be substitutable for the bounds. + if (const auto *typeArgObjC = typeArg->getAs<ObjCObjectPointerType>()) { + // If we don't have a type parameter to match against, assume + // everything is fine. There was a prior pack expansion that + // means we won't be able to match anything. + if (!typeParam) { + assert(anyPackExpansions && "Too many arguments?"); + continue; + } + + // Retrieve the bound. + QualType bound = typeParam->getUnderlyingType(); + const auto *boundObjC = bound->castAs<ObjCObjectPointerType>(); + + // Determine whether the type argument is substitutable for the bound. + if (typeArgObjC->isObjCIdType()) { + // When the type argument is 'id', the only acceptable type + // parameter bound is 'id'. + if (boundObjC->isObjCIdType()) + continue; + } else if (S.Context.canAssignObjCInterfaces(boundObjC, typeArgObjC)) { + // Otherwise, we follow the assignability rules. + continue; + } + + // Diagnose the mismatch. + S.Diag(typeArgInfo->getTypeLoc().getBeginLoc(), + diag::err_objc_type_arg_does_not_match_bound) + << typeArg << bound << typeParam->getDeclName(); + S.Diag(typeParam->getLocation(), diag::note_objc_type_param_here) + << typeParam->getDeclName(); + + if (failOnError) + return QualType(); + + return type; + } + + // Block pointer types are permitted for unqualified 'id' bounds. + if (typeArg->isBlockPointerType()) { + // If we don't have a type parameter to match against, assume + // everything is fine. There was a prior pack expansion that + // means we won't be able to match anything. + if (!typeParam) { + assert(anyPackExpansions && "Too many arguments?"); + continue; + } + + // Retrieve the bound. + QualType bound = typeParam->getUnderlyingType(); + if (bound->isBlockCompatibleObjCPointerType(S.Context)) + continue; + + // Diagnose the mismatch. + S.Diag(typeArgInfo->getTypeLoc().getBeginLoc(), + diag::err_objc_type_arg_does_not_match_bound) + << typeArg << bound << typeParam->getDeclName(); + S.Diag(typeParam->getLocation(), diag::note_objc_type_param_here) + << typeParam->getDeclName(); + + if (failOnError) + return QualType(); + + return type; + } + + // Types that have __attribute__((NSObject)) are permitted. + if (typeArg->isObjCNSObjectType()) { + continue; + } + + // Dependent types will be checked at instantiation time. + if (typeArg->isDependentType()) { + continue; + } + + // Diagnose non-id-compatible type arguments. + S.Diag(typeArgInfo->getTypeLoc().getBeginLoc(), + diag::err_objc_type_arg_not_id_compatible) + << typeArg << typeArgInfo->getTypeLoc().getSourceRange(); + + if (failOnError) + return QualType(); + + return type; + } + + // Make sure we didn't have the wrong number of arguments. + if (!anyPackExpansions && finalTypeArgs.size() != numTypeParams) { + S.Diag(loc, diag::err_objc_type_args_wrong_arity) + << (typeArgs.size() < typeParams->size()) << objcClass->getDeclName() + << (unsigned)finalTypeArgs.size() << (unsigned)numTypeParams; + S.Diag(objcClass->getLocation(), diag::note_previous_decl) << objcClass; + + if (failOnError) + return QualType(); + + return type; + } + + // Success. Form the specialized type. + return S.Context.getObjCObjectType(type, finalTypeArgs, {}, false); +} + +QualType SemaObjC::BuildObjCObjectType( + QualType BaseType, SourceLocation Loc, SourceLocation TypeArgsLAngleLoc, + ArrayRef<TypeSourceInfo *> TypeArgs, SourceLocation TypeArgsRAngleLoc, + SourceLocation ProtocolLAngleLoc, ArrayRef<ObjCProtocolDecl *> Protocols, + ArrayRef<SourceLocation> ProtocolLocs, SourceLocation ProtocolRAngleLoc, + bool FailOnError, bool Rebuilding) { + ASTContext &Context = getASTContext(); + QualType Result = BaseType; + if (!TypeArgs.empty()) { + Result = + applyObjCTypeArgs(SemaRef, Loc, Result, TypeArgs, + SourceRange(TypeArgsLAngleLoc, TypeArgsRAngleLoc), + FailOnError, Rebuilding); + if (FailOnError && Result.isNull()) + return QualType(); + } + + if (!Protocols.empty()) { + bool HasError; + Result = Context.applyObjCProtocolQualifiers(Result, Protocols, HasError); + if (HasError) { + Diag(Loc, diag::err_invalid_protocol_qualifiers) + << SourceRange(ProtocolLAngleLoc, ProtocolRAngleLoc); + if (FailOnError) + Result = QualType(); + } + if (FailOnError && Result.isNull()) + return QualType(); + } + + return Result; +} + +ParsedType SemaObjC::ActOnObjCInstanceType(SourceLocation Loc) { + ASTContext &Context = getASTContext(); + QualType T = Context.getObjCInstanceType(); + TypeSourceInfo *TInfo = Context.getTrivialTypeSourceInfo(T, Loc); + return SemaRef.CreateParsedType(T, TInfo); +} + +//===--- CHECK: Objective-C retain cycles ----------------------------------// + +namespace { + +struct RetainCycleOwner { + VarDecl *Variable = nullptr; + SourceRange Range; + SourceLocation Loc; + bool Indirect = false; + + RetainCycleOwner() = default; + + void setLocsFrom(Expr *e) { + Loc = e->getExprLoc(); + Range = e->getSourceRange(); + } +}; + +} // namespace + +/// Consider whether capturing the given variable can possibly lead to +/// a retain cycle. +static bool considerVariable(VarDecl *var, Expr *ref, RetainCycleOwner &owner) { + // In ARC, it's captured strongly iff the variable has __strong + // lifetime. In MRR, it's captured strongly if the variable is + // __block and has an appropriate type. + if (var->getType().getObjCLifetime() != Qualifiers::OCL_Strong) + return false; + + owner.Variable = var; + if (ref) + owner.setLocsFrom(ref); + return true; +} + +static bool findRetainCycleOwner(Sema &S, Expr *e, RetainCycleOwner &owner) { + while (true) { + e = e->IgnoreParens(); + if (CastExpr *cast = dyn_cast<CastExpr>(e)) { + switch (cast->getCastKind()) { + case CK_BitCast: + case CK_LValueBitCast: + case CK_LValueToRValue: + case CK_ARCReclaimReturnedObject: + e = cast->getSubExpr(); + continue; + + default: + return false; + } + } + + if (ObjCIvarRefExpr *ref = dyn_cast<ObjCIvarRefExpr>(e)) { + ObjCIvarDecl *ivar = ref->getDecl(); + if (ivar->getType().getObjCLifetime() != Qualifiers::OCL_Strong) + return false; + + // Try to find a retain cycle in the base. + if (!findRetainCycleOwner(S, ref->getBase(), owner)) + return false; + + if (ref->isFreeIvar()) + owner.setLocsFrom(ref); + owner.Indirect = true; + return true; + } + + if (DeclRefExpr *ref = dyn_cast<DeclRefExpr>(e)) { + VarDecl *var = dyn_cast<VarDecl>(ref->getDecl()); + if (!var) + return false; + return considerVariable(var, ref, owner); + } + + if (MemberExpr *member = dyn_cast<MemberExpr>(e)) { + if (member->isArrow()) + return false; + + // Don't count this as an indirect ownership. + e = member->getBase(); + continue; + } + + if (PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(e)) { + // Only pay attention to pseudo-objects on property references. + ObjCPropertyRefExpr *pre = dyn_cast<ObjCPropertyRefExpr>( + pseudo->getSyntacticForm()->IgnoreParens()); + if (!pre) + return false; + if (pre->isImplicitProperty()) + return false; + ObjCPropertyDecl *property = pre->getExplicitProperty(); + if (!property->isRetaining() && + !(property->getPropertyIvarDecl() && + property->getPropertyIvarDecl()->getType().getObjCLifetime() == + Qualifiers::OCL_Strong)) + return false; + + owner.Indirect = true; + if (pre->isSuperReceiver()) { + owner.Variable = S.getCurMethodDecl()->getSelfDecl(); + if (!owner.Variable) + return false; + owner.Loc = pre->getLocation(); + owner.Range = pre->getSourceRange(); + return true; + } + e = const_cast<Expr *>( + cast<OpaqueValueExpr>(pre->getBase())->getSourceExpr()); + continue; + } + + // Array ivars? + + return false; + } +} + +namespace { + +struct FindCaptureVisitor : EvaluatedExprVisitor<FindCaptureVisitor> { + VarDecl *Variable; + Expr *Capturer = nullptr; + bool VarWillBeReased = false; + + FindCaptureVisitor(ASTContext &Context, VarDecl *variable) + : EvaluatedExprVisitor<FindCaptureVisitor>(Context), Variable(variable) {} + + void VisitDeclRefExpr(DeclRefExpr *ref) { + if (ref->getDecl() == Variable && !Capturer) + Capturer = ref; + } + + void VisitObjCIvarRefExpr(ObjCIvarRefExpr *ref) { + if (Capturer) + return; + Visit(ref->getBase()); + if (Capturer && ref->isFreeIvar()) + Capturer = ref; + } + + void VisitBlockExpr(BlockExpr *block) { + // Look inside nested blocks + if (block->getBlockDecl()->capturesVariable(Variable)) + Visit(block->getBlockDecl()->getBody()); + } + + void VisitOpaqueValueExpr(OpaqueValueExpr *OVE) { + if (Capturer) + return; + if (OVE->getSourceExpr()) + Visit(OVE->getSourceExpr()); + } + + void VisitBinaryOperator(BinaryOperator *BinOp) { + if (!Variable || VarWillBeReased || BinOp->getOpcode() != BO_Assign) + return; + Expr *LHS = BinOp->getLHS(); + if (const DeclRefExpr *DRE = dyn_cast_or_null<DeclRefExpr>(LHS)) { + if (DRE->getDecl() != Variable) + return; + if (Expr *RHS = BinOp->getRHS()) { + RHS = RHS->IgnoreParenCasts(); + std::optional<llvm::APSInt> Value; + VarWillBeReased = + (RHS && (Value = RHS->getIntegerConstantExpr(Context)) && + *Value == 0); + } + } + } +}; + +} // namespace + +/// Check whether the given argument is a block which captures a +/// variable. +static Expr *findCapturingExpr(Sema &S, Expr *e, RetainCycleOwner &owner) { + assert(owner.Variable && owner.Loc.isValid()); + + e = e->IgnoreParenCasts(); + + // Look through [^{...} copy] and Block_copy(^{...}). + if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(e)) { + Selector Cmd = ME->getSelector(); + if (Cmd.isUnarySelector() && Cmd.getNameForSlot(0) == "copy") { + e = ME->getInstanceReceiver(); + if (!e) + return nullptr; + e = e->IgnoreParenCasts(); + } + } else if (CallExpr *CE = dyn_cast<CallExpr>(e)) { + if (CE->getNumArgs() == 1) { + FunctionDecl *Fn = dyn_cast_or_null<FunctionDecl>(CE->getCalleeDecl()); + if (Fn) { + const IdentifierInfo *FnI = Fn->getIdentifier(); + if (FnI && FnI->isStr("_Block_copy")) { + e = CE->getArg(0)->IgnoreParenCasts(); + } + } + } + } + + BlockExpr *block = dyn_cast<BlockExpr>(e); + if (!block || !block->getBlockDecl()->capturesVariable(owner.Variable)) + return nullptr; + + FindCaptureVisitor visitor(S.Context, owner.Variable); + visitor.Visit(block->getBlockDecl()->getBody()); + return visitor.VarWillBeReased ? nullptr : visitor.Capturer; +} + +static void diagnoseRetainCycle(Sema &S, Expr *capturer, + RetainCycleOwner &owner) { + assert(capturer); + assert(owner.Variable && owner.Loc.isValid()); + + S.Diag(capturer->getExprLoc(), diag::warn_arc_retain_cycle) + << owner.Variable << capturer->getSourceRange(); + S.Diag(owner.Loc, diag::note_arc_retain_cycle_owner) + << owner.Indirect << owner.Range; +} + +/// Check for a keyword selector that starts with the word 'add' or +/// 'set'. +static bool isSetterLikeSelector(Selector sel) { + if (sel.isUnarySelector()) + return false; + + StringRef str = sel.getNameForSlot(0); + str = str.ltrim('_'); + if (str.starts_with("set")) + str = str.substr(3); + else if (str.starts_with("add")) { + // Specially allow 'addOperationWithBlock:'. + if (sel.getNumArgs() == 1 && str.starts_with("addOperationWithBlock")) + return false; + str = str.substr(3); + } else + return false; + + if (str.empty()) + return true; + return !isLowercase(str.front()); +} + +static std::optional<int> +GetNSMutableArrayArgumentIndex(SemaObjC &S, ObjCMessageExpr *Message) { + bool IsMutableArray = S.NSAPIObj->isSubclassOfNSClass( + Message->getReceiverInterface(), NSAPI::ClassId_NSMutableArray); + if (!IsMutableArray) { + return std::nullopt; + } + + Selector Sel = Message->getSelector(); + + std::optional<NSAPI::NSArrayMethodKind> MKOpt = + S.NSAPIObj->getNSArrayMethodKind(Sel); + if (!MKOpt) { + return std::nullopt; + } + + NSAPI::NSArrayMethodKind MK = *MKOpt; + + switch (MK) { + case NSAPI::NSMutableArr_addObject: + case NSAPI::NSMutableArr_insertObjectAtIndex: + case NSAPI::NSMutableArr_setObjectAtIndexedSubscript: + return 0; + case NSAPI::NSMutableArr_replaceObjectAtIndex: + return 1; + + default: + return std::nullopt; + } + + return std::nullopt; +} + +static std::optional<int> +GetNSMutableDictionaryArgumentIndex(SemaObjC &S, ObjCMessageExpr *Message) { + bool IsMutableDictionary = S.NSAPIObj->isSubclassOfNSClass( + Message->getReceiverInterface(), NSAPI::ClassId_NSMutableDictionary); + if (!IsMutableDictionary) { + return std::nullopt; + } + + Selector Sel = Message->getSelector(); + + std::optional<NSAPI::NSDictionaryMethodKind> MKOpt = + S.NSAPIObj->getNSDictionaryMethodKind(Sel); + if (!MKOpt) { + return std::nullopt; + } + + NSAPI::NSDictionaryMethodKind MK = *MKOpt; + + switch (MK) { + case NSAPI::NSMutableDict_setObjectForKey: + case NSAPI::NSMutableDict_setValueForKey: + case NSAPI::NSMutableDict_setObjectForKeyedSubscript: + return 0; + + default: + return std::nullopt; + } + + return std::nullopt; +} + +static std::optional<int> GetNSSetArgumentIndex(SemaObjC &S, + ObjCMessageExpr *Message) { + bool IsMutableSet = S.NSAPIObj->isSubclassOfNSClass( + Message->getReceiverInterface(), NSAPI::ClassId_NSMutableSet); + + bool IsMutableOrderedSet = S.NSAPIObj->isSubclassOfNSClass( + Message->getReceiverInterface(), NSAPI::ClassId_NSMutableOrderedSet); + if (!IsMutableSet && !IsMutableOrderedSet) { + return std::nullopt; + } + + Selector Sel = Message->getSelector(); + + std::optional<NSAPI::NSSetMethodKind> MKOpt = + S.NSAPIObj->getNSSetMethodKind(Sel); + if (!MKOpt) { + return std::nullopt; + } + + NSAPI::NSSetMethodKind MK = *MKOpt; + + switch (MK) { + case NSAPI::NSMutableSet_addObject: + case NSAPI::NSOrderedSet_setObjectAtIndex: + case NSAPI::NSOrderedSet_setObjectAtIndexedSubscript: + case NSAPI::NSOrderedSet_insertObjectAtIndex: + return 0; + case NSAPI::NSOrderedSet_replaceObjectAtIndexWithObject: + return 1; + } + + return std::nullopt; +} + +void SemaObjC::CheckObjCCircularContainer(ObjCMessageExpr *Message) { + if (!Message->isInstanceMessage()) { + return; + } + + std::optional<int> ArgOpt; + + if (!(ArgOpt = GetNSMutableArrayArgumentIndex(*this, Message)) && + !(ArgOpt = GetNSMutableDictionaryArgumentIndex(*this, Message)) && + !(ArgOpt = GetNSSetArgumentIndex(*this, Message))) { + return; + } + + int ArgIndex = *ArgOpt; + + Expr *Arg = Message->getArg(ArgIndex)->IgnoreImpCasts(); + if (OpaqueValueExpr *OE = dyn_cast<OpaqueValueExpr>(Arg)) { + Arg = OE->getSourceExpr()->IgnoreImpCasts(); + } + + if (Message->getReceiverKind() == ObjCMessageExpr::SuperInstance) { + if (DeclRefExpr *ArgRE = dyn_cast<DeclRefExpr>(Arg)) { + if (ArgRE->isObjCSelfExpr()) { + Diag(Message->getSourceRange().getBegin(), + diag::warn_objc_circular_container) + << ArgRE->getDecl() << StringRef("'super'"); + } + } + } else { + Expr *Receiver = Message->getInstanceReceiver()->IgnoreImpCasts(); + + if (OpaqueValueExpr *OE = dyn_cast<OpaqueValueExpr>(Receiver)) { + Receiver = OE->getSourceExpr()->IgnoreImpCasts(); + } + + if (DeclRefExpr *ReceiverRE = dyn_cast<DeclRefExpr>(Receiver)) { + if (DeclRefExpr *ArgRE = dyn_cast<DeclRefExpr>(Arg)) { + if (ReceiverRE->getDecl() == ArgRE->getDecl()) { + ValueDecl *Decl = ReceiverRE->getDecl(); + Diag(Message->getSourceRange().getBegin(), + diag::warn_objc_circular_container) + << Decl << Decl; + if (!ArgRE->isObjCSelfExpr()) { + Diag(Decl->getLocation(), + diag::note_objc_circular_container_declared_here) + << Decl; + } + } + } + } else if (ObjCIvarRefExpr *IvarRE = dyn_cast<ObjCIvarRefExpr>(Receiver)) { + if (ObjCIvarRefExpr *IvarArgRE = dyn_cast<ObjCIvarRefExpr>(Arg)) { + if (IvarRE->getDecl() == IvarArgRE->getDecl()) { + ObjCIvarDecl *Decl = IvarRE->getDecl(); + Diag(Message->getSourceRange().getBegin(), + diag::warn_objc_circular_container) + << Decl << Decl; + Diag(Decl->getLocation(), + diag::note_objc_circular_container_declared_here) + << Decl; + } + } + } + } +} + +/// Check a message send to see if it's likely to cause a retain cycle. +void SemaObjC::checkRetainCycles(ObjCMessageExpr *msg) { + // Only check instance methods whose selector looks like a setter. + if (!msg->isInstanceMessage() || !isSetterLikeSelector(msg->getSelector())) + return; + + // Try to find a variable that the receiver is strongly owned by. + RetainCycleOwner owner; + if (msg->getReceiverKind() == ObjCMessageExpr::Instance) { + if (!findRetainCycleOwner(SemaRef, msg->getInstanceReceiver(), owner)) + return; + } else { + assert(msg->getReceiverKind() == ObjCMessageExpr::SuperInstance); + owner.Variable = SemaRef.getCurMethodDecl()->getSelfDecl(); + owner.Loc = msg->getSuperLoc(); + owner.Range = msg->getSuperLoc(); + } + + // Check whether the receiver is captured by any of the arguments. + const ObjCMethodDecl *MD = msg->getMethodDecl(); + for (unsigned i = 0, e = msg->getNumArgs(); i != e; ++i) { + if (Expr *capturer = findCapturingExpr(SemaRef, msg->getArg(i), owner)) { + // noescape blocks should not be retained by the method. + if (MD && MD->parameters()[i]->hasAttr<NoEscapeAttr>()) + continue; + return diagnoseRetainCycle(SemaRef, capturer, owner); + } + } +} + +/// Check a property assign to see if it's likely to cause a retain cycle. +void SemaObjC::checkRetainCycles(Expr *receiver, Expr *argument) { + RetainCycleOwner owner; + if (!findRetainCycleOwner(SemaRef, receiver, owner)) + return; + + if (Expr *capturer = findCapturingExpr(SemaRef, argument, owner)) + diagnoseRetainCycle(SemaRef, capturer, owner); +} + +void SemaObjC::checkRetainCycles(VarDecl *Var, Expr *Init) { + RetainCycleOwner Owner; + if (!considerVariable(Var, /*DeclRefExpr=*/nullptr, Owner)) + return; + + // Because we don't have an expression for the variable, we have to set the + // location explicitly here. + Owner.Loc = Var->getLocation(); + Owner.Range = Var->getSourceRange(); + + if (Expr *Capturer = findCapturingExpr(SemaRef, Init, Owner)) + diagnoseRetainCycle(SemaRef, Capturer, Owner); +} + +/// CheckObjCString - Checks that the argument to the builtin +/// CFString constructor is correct +/// Note: It might also make sense to do the UTF-16 conversion here (would +/// simplify the backend). +bool SemaObjC::CheckObjCString(Expr *Arg) { + Arg = Arg->IgnoreParenCasts(); + StringLiteral *Literal = dyn_cast<StringLiteral>(Arg); + + if (!Literal || !Literal->isOrdinary()) { + Diag(Arg->getBeginLoc(), diag::err_cfstring_literal_not_string_constant) + << Arg->getSourceRange(); + return true; + } + + if (Literal->containsNonAsciiOrNull()) { + StringRef String = Literal->getString(); + unsigned NumBytes = String.size(); + SmallVector<llvm::UTF16, 128> ToBuf(NumBytes); + const llvm::UTF8 *FromPtr = (const llvm::UTF8 *)String.data(); + llvm::UTF16 *ToPtr = &ToBuf[0]; + + llvm::ConversionResult Result = + llvm::ConvertUTF8toUTF16(&FromPtr, FromPtr + NumBytes, &ToPtr, + ToPtr + NumBytes, llvm::strictConversion); + // Check for conversion failure. + if (Result != llvm::conversionOK) + Diag(Arg->getBeginLoc(), diag::warn_cfstring_truncated) + << Arg->getSourceRange(); + } + return false; +} + +bool SemaObjC::CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation lbrac, + ArrayRef<const Expr *> Args) { + Sema::VariadicCallType CallType = + Method->isVariadic() ? Sema::VariadicMethod : Sema::VariadicDoesNotApply; + + SemaRef.checkCall(Method, nullptr, /*ThisArg=*/nullptr, Args, + /*IsMemberFunction=*/false, lbrac, Method->getSourceRange(), + CallType); + + SemaRef.CheckTCBEnforcement(lbrac, Method); + + return false; +} + +const DeclContext *SemaObjC::getCurObjCLexicalContext() const { + const DeclContext *DC = SemaRef.getCurLexicalContext(); + // A category implicitly has the attribute of the interface. + if (const ObjCCategoryDecl *CatD = dyn_cast<ObjCCategoryDecl>(DC)) + DC = CatD->getClassInterface(); + return DC; +} + +/// Retrieve the identifier "NSError". +IdentifierInfo *SemaObjC::getNSErrorIdent() { + if (!Ident_NSError) + Ident_NSError = SemaRef.PP.getIdentifierInfo("NSError"); + + return Ident_NSError; +} + +void SemaObjC::ActOnObjCContainerStartDefinition(ObjCContainerDecl *IDecl) { + assert( + IDecl->getLexicalParent() == SemaRef.CurContext && + "The next DeclContext should be lexically contained in the current one."); + SemaRef.CurContext = IDecl; +} + +void SemaObjC::ActOnObjCContainerFinishDefinition() { + // Exit this scope of this interface definition. + SemaRef.PopDeclContext(); +} + +void SemaObjC::ActOnObjCTemporaryExitContainerContext( + ObjCContainerDecl *ObjCCtx) { + assert(ObjCCtx == SemaRef.CurContext && "Mismatch of container contexts"); + SemaRef.OriginalLexicalContext = ObjCCtx; + ActOnObjCContainerFinishDefinition(); +} + +void SemaObjC::ActOnObjCReenterContainerContext(ObjCContainerDecl *ObjCCtx) { + ActOnObjCContainerStartDefinition(ObjCCtx); + SemaRef.OriginalLexicalContext = nullptr; +} + +/// Find the protocol with the given name, if any. +ObjCProtocolDecl *SemaObjC::LookupProtocol(IdentifierInfo *II, + SourceLocation IdLoc, + RedeclarationKind Redecl) { + Decl *D = SemaRef.LookupSingleName(SemaRef.TUScope, II, IdLoc, + Sema::LookupObjCProtocolName, Redecl); + return cast_or_null<ObjCProtocolDecl>(D); +} + +/// Determine whether this is an Objective-C writeback conversion, +/// used for parameter passing when performing automatic reference counting. +/// +/// \param FromType The type we're converting form. +/// +/// \param ToType The type we're converting to. +/// +/// \param ConvertedType The type that will be produced after applying +/// this conversion. +bool SemaObjC::isObjCWritebackConversion(QualType FromType, QualType ToType, + QualType &ConvertedType) { + ASTContext &Context = getASTContext(); + if (!getLangOpts().ObjCAutoRefCount || + Context.hasSameUnqualifiedType(FromType, ToType)) + return false; + + // Parameter must be a pointer to __autoreleasing (with no other qualifiers). + QualType ToPointee; + if (const PointerType *ToPointer = ToType->getAs<PointerType>()) + ToPointee = ToPointer->getPointeeType(); + else + return false; + + Qualifiers ToQuals = ToPointee.getQualifiers(); + if (!ToPointee->isObjCLifetimeType() || + ToQuals.getObjCLifetime() != Qualifiers::OCL_Autoreleasing || + !ToQuals.withoutObjCLifetime().empty()) + return false; + + // Argument must be a pointer to __strong to __weak. + QualType FromPointee; + if (const PointerType *FromPointer = FromType->getAs<PointerType>()) + FromPointee = FromPointer->getPointeeType(); + else + return false; + + Qualifiers FromQuals = FromPointee.getQualifiers(); + if (!FromPointee->isObjCLifetimeType() || + (FromQuals.getObjCLifetime() != Qualifiers::OCL_Strong && + FromQuals.getObjCLifetime() != Qualifiers::OCL_Weak)) + return false; + + // Make sure that we have compatible qualifiers. + FromQuals.setObjCLifetime(Qualifiers::OCL_Autoreleasing); + if (!ToQuals.compatiblyIncludes(FromQuals)) + return false; + + // Remove qualifiers from the pointee type we're converting from; they + // aren't used in the compatibility check belong, and we'll be adding back + // qualifiers (with __autoreleasing) if the compatibility check succeeds. + FromPointee = FromPointee.getUnqualifiedType(); + + // The unqualified form of the pointee types must be compatible. + ToPointee = ToPointee.getUnqualifiedType(); + bool IncompatibleObjC; + if (Context.typesAreCompatible(FromPointee, ToPointee)) + FromPointee = ToPointee; + else if (!SemaRef.isObjCPointerConversion(FromPointee, ToPointee, FromPointee, + IncompatibleObjC)) + return false; + + /// Construct the type we're converting to, which is a pointer to + /// __autoreleasing pointee. + FromPointee = Context.getQualifiedType(FromPointee, FromQuals); + ConvertedType = Context.getPointerType(FromPointee); + return true; +} + +/// CheckSubscriptingKind - This routine decide what type +/// of indexing represented by "FromE" is being done. +SemaObjC::ObjCSubscriptKind SemaObjC::CheckSubscriptingKind(Expr *FromE) { + // If the expression already has integral or enumeration type, we're golden. + QualType T = FromE->getType(); + if (T->isIntegralOrEnumerationType()) + return SemaObjC::OS_Array; + + // If we don't have a class type in C++, there's no way we can get an + // expression of integral or enumeration type. + const RecordType *RecordTy = T->getAs<RecordType>(); + if (!RecordTy && (T->isObjCObjectPointerType() || T->isVoidPointerType())) + // All other scalar cases are assumed to be dictionary indexing which + // caller handles, with diagnostics if needed. + return SemaObjC::OS_Dictionary; + if (!getLangOpts().CPlusPlus || !RecordTy || RecordTy->isIncompleteType()) { + // No indexing can be done. Issue diagnostics and quit. + const Expr *IndexExpr = FromE->IgnoreParenImpCasts(); + if (isa<StringLiteral>(IndexExpr)) + Diag(FromE->getExprLoc(), diag::err_objc_subscript_pointer) + << T << FixItHint::CreateInsertion(FromE->getExprLoc(), "@"); + else + Diag(FromE->getExprLoc(), diag::err_objc_subscript_type_conversion) << T; + return SemaObjC::OS_Error; + } + + // We must have a complete class type. + if (SemaRef.RequireCompleteType(FromE->getExprLoc(), T, + diag::err_objc_index_incomplete_class_type, + FromE)) + return SemaObjC::OS_Error; + + // Look for a conversion to an integral, enumeration type, or + // objective-C pointer type. + int NoIntegrals = 0, NoObjCIdPointers = 0; + SmallVector<CXXConversionDecl *, 4> ConversionDecls; + + for (NamedDecl *D : cast<CXXRecordDecl>(RecordTy->getDecl()) + ->getVisibleConversionFunctions()) { + if (CXXConversionDecl *Conversion = + dyn_cast<CXXConversionDecl>(D->getUnderlyingDecl())) { + QualType CT = Conversion->getConversionType().getNonReferenceType(); + if (CT->isIntegralOrEnumerationType()) { + ++NoIntegrals; + ConversionDecls.push_back(Conversion); + } else if (CT->isObjCIdType() || CT->isBlockPointerType()) { + ++NoObjCIdPointers; + ConversionDecls.push_back(Conversion); + } + } + } + if (NoIntegrals == 1 && NoObjCIdPointers == 0) + return SemaObjC::OS_Array; + if (NoIntegrals == 0 && NoObjCIdPointers == 1) + return SemaObjC::OS_Dictionary; + if (NoIntegrals == 0 && NoObjCIdPointers == 0) { + // No conversion function was found. Issue diagnostic and return. + Diag(FromE->getExprLoc(), diag::err_objc_subscript_type_conversion) + << FromE->getType(); + return SemaObjC::OS_Error; + } + Diag(FromE->getExprLoc(), diag::err_objc_multiple_subscript_type_conversion) + << FromE->getType(); + for (unsigned int i = 0; i < ConversionDecls.size(); i++) + Diag(ConversionDecls[i]->getLocation(), + diag::note_conv_function_declared_at); + + return SemaObjC::OS_Error; +} + +void SemaObjC::AddCFAuditedAttribute(Decl *D) { + ASTContext &Context = getASTContext(); + IdentifierInfo *Ident; + SourceLocation Loc; + std::tie(Ident, Loc) = SemaRef.PP.getPragmaARCCFCodeAuditedInfo(); + if (!Loc.isValid()) + return; + + // Don't add a redundant or conflicting attribute. + if (D->hasAttr<CFAuditedTransferAttr>() || + D->hasAttr<CFUnknownTransferAttr>()) + return; + + AttributeCommonInfo Info(Ident, SourceRange(Loc), + AttributeCommonInfo::Form::Pragma()); + D->addAttr(CFAuditedTransferAttr::CreateImplicit(Context, Info)); +} + +bool SemaObjC::isCFError(RecordDecl *RD) { + // If we already know about CFError, test it directly. + if (CFError) + return CFError == RD; + + // Check whether this is CFError, which we identify based on its bridge to + // NSError. CFErrorRef used to be declared with "objc_bridge" but is now + // declared with "objc_bridge_mutable", so look for either one of the two + // attributes. + if (RD->getTagKind() == TagTypeKind::Struct) { + IdentifierInfo *bridgedType = nullptr; + if (auto bridgeAttr = RD->getAttr<ObjCBridgeAttr>()) + bridgedType = bridgeAttr->getBridgedType(); + else if (auto bridgeAttr = RD->getAttr<ObjCBridgeMutableAttr>()) + bridgedType = bridgeAttr->getBridgedType(); + + if (bridgedType == getNSErrorIdent()) { + CFError = RD; + return true; + } + } + + return false; +} + +bool SemaObjC::isNSStringType(QualType T, bool AllowNSAttributedString) { + const auto *PT = T->getAs<ObjCObjectPointerType>(); + if (!PT) + return false; + + ObjCInterfaceDecl *Cls = PT->getObjectType()->getInterface(); + if (!Cls) + return false; + + IdentifierInfo *ClsName = Cls->getIdentifier(); + + if (AllowNSAttributedString && + ClsName == &getASTContext().Idents.get("NSAttributedString")) + return true; + // FIXME: Should we walk the chain of classes? + return ClsName == &getASTContext().Idents.get("NSString") || + ClsName == &getASTContext().Idents.get("NSMutableString"); +} + +bool SemaObjC::isCFStringType(QualType T) { + const auto *PT = T->getAs<PointerType>(); + if (!PT) + return false; + + const auto *RT = PT->getPointeeType()->getAs<RecordType>(); + if (!RT) + return false; + + const RecordDecl *RD = RT->getDecl(); + if (RD->getTagKind() != TagTypeKind::Struct) + return false; + + return RD->getIdentifier() == &getASTContext().Idents.get("__CFString"); +} + +static bool checkIBOutletCommon(Sema &S, Decl *D, const ParsedAttr &AL) { + // The IBOutlet/IBOutletCollection attributes only apply to instance + // variables or properties of Objective-C classes. The outlet must also + // have an object reference type. + if (const auto *VD = dyn_cast<ObjCIvarDecl>(D)) { + if (!VD->getType()->getAs<ObjCObjectPointerType>()) { + S.Diag(AL.getLoc(), diag::warn_iboutlet_object_type) + << AL << VD->getType() << 0; + return false; + } + } else if (const auto *PD = dyn_cast<ObjCPropertyDecl>(D)) { + if (!PD->getType()->getAs<ObjCObjectPointerType>()) { + S.Diag(AL.getLoc(), diag::warn_iboutlet_object_type) + << AL << PD->getType() << 1; + return false; + } + } else { + S.Diag(AL.getLoc(), diag::warn_attribute_iboutlet) << AL; + return false; + } + + return true; +} + +void SemaObjC::handleIBOutlet(Decl *D, const ParsedAttr &AL) { + if (!checkIBOutletCommon(SemaRef, D, AL)) + return; + + D->addAttr(::new (getASTContext()) IBOutletAttr(getASTContext(), AL)); +} + +void SemaObjC::handleIBOutletCollection(Decl *D, const ParsedAttr &AL) { + + ASTContext &Context = getASTContext(); + // The iboutletcollection attribute can have zero or one arguments. + if (AL.getNumArgs() > 1) { + Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1; + return; + } + + if (!checkIBOutletCommon(SemaRef, D, AL)) + return; + + ParsedType PT; + + if (AL.hasParsedType()) + PT = AL.getTypeArg(); + else { + PT = SemaRef.getTypeName( + Context.Idents.get("NSObject"), AL.getLoc(), + SemaRef.getScopeForContext(D->getDeclContext()->getParent())); + if (!PT) { + Diag(AL.getLoc(), diag::err_iboutletcollection_type) << "NSObject"; + return; + } + } + + TypeSourceInfo *QTLoc = nullptr; + QualType QT = SemaRef.GetTypeFromParser(PT, &QTLoc); + if (!QTLoc) + QTLoc = Context.getTrivialTypeSourceInfo(QT, AL.getLoc()); + + // Diagnose use of non-object type in iboutletcollection attribute. + // FIXME. Gnu attribute extension ignores use of builtin types in + // attributes. So, __attribute__((iboutletcollection(char))) will be + // treated as __attribute__((iboutletcollection())). + if (!QT->isObjCIdType() && !QT->isObjCObjectType()) { + Diag(AL.getLoc(), QT->isBuiltinType() + ? diag::err_iboutletcollection_builtintype + : diag::err_iboutletcollection_type) + << QT; + return; + } + + D->addAttr(::new (Context) IBOutletCollectionAttr(Context, AL, QTLoc)); +} + +void SemaObjC::handleSuppresProtocolAttr(Decl *D, const ParsedAttr &AL) { + if (!cast<ObjCProtocolDecl>(D)->isThisDeclarationADefinition()) { + Diag(AL.getLoc(), diag::err_objc_attr_protocol_requires_definition) + << AL << AL.getRange(); + return; + } + + D->addAttr(::new (getASTContext()) + ObjCExplicitProtocolImplAttr(getASTContext(), AL)); +} + +void SemaObjC::handleDirectAttr(Decl *D, const ParsedAttr &AL) { + // objc_direct cannot be set on methods declared in the context of a protocol + if (isa<ObjCProtocolDecl>(D->getDeclContext())) { + Diag(AL.getLoc(), diag::err_objc_direct_on_protocol) << false; + return; + } + + if (getLangOpts().ObjCRuntime.allowsDirectDispatch()) { + handleSimpleAttribute<ObjCDirectAttr>(*this, D, AL); + } else { + Diag(AL.getLoc(), diag::warn_objc_direct_ignored) << AL; + } +} + +void SemaObjC::handleDirectMembersAttr(Decl *D, const ParsedAttr &AL) { + if (getLangOpts().ObjCRuntime.allowsDirectDispatch()) { + handleSimpleAttribute<ObjCDirectMembersAttr>(*this, D, AL); + } else { + Diag(AL.getLoc(), diag::warn_objc_direct_ignored) << AL; + } +} + +void SemaObjC::handleMethodFamilyAttr(Decl *D, const ParsedAttr &AL) { + const auto *M = cast<ObjCMethodDecl>(D); + if (!AL.isArgIdent(0)) { + Diag(AL.getLoc(), diag::err_attribute_argument_n_type) + << AL << 1 << AANT_ArgumentIdentifier; + return; + } + + IdentifierLoc *IL = AL.getArgAsIdent(0); + ObjCMethodFamilyAttr::FamilyKind F; + if (!ObjCMethodFamilyAttr::ConvertStrToFamilyKind(IL->Ident->getName(), F)) { + Diag(IL->Loc, diag::warn_attribute_type_not_supported) << AL << IL->Ident; + return; + } + + if (F == ObjCMethodFamilyAttr::OMF_init && + !M->getReturnType()->isObjCObjectPointerType()) { + Diag(M->getLocation(), diag::err_init_method_bad_return_type) + << M->getReturnType(); + // Ignore the attribute. + return; + } + + D->addAttr(new (getASTContext()) + ObjCMethodFamilyAttr(getASTContext(), AL, F)); +} + +void SemaObjC::handleNSObject(Decl *D, const ParsedAttr &AL) { + if (const auto *TD = dyn_cast<TypedefNameDecl>(D)) { + QualType T = TD->getUnderlyingType(); + if (!T->isCARCBridgableType()) { + Diag(TD->getLocation(), diag::err_nsobject_attribute); + return; + } + } else if (const auto *PD = dyn_cast<ObjCPropertyDecl>(D)) { + QualType T = PD->getType(); + if (!T->isCARCBridgableType()) { + 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. + Diag(D->getLocation(), diag::warn_nsobject_attribute); + } + D->addAttr(::new (getASTContext()) ObjCNSObjectAttr(getASTContext(), AL)); +} + +void SemaObjC::handleIndependentClass(Decl *D, const ParsedAttr &AL) { + if (const auto *TD = dyn_cast<TypedefNameDecl>(D)) { + QualType T = TD->getUnderlyingType(); + if (!T->isObjCObjectPointerType()) { + Diag(TD->getLocation(), diag::warn_ptr_independentclass_attribute); + return; + } + } else { + Diag(D->getLocation(), diag::warn_independentclass_attribute); + return; + } + D->addAttr(::new (getASTContext()) + ObjCIndependentClassAttr(getASTContext(), AL)); +} + +void SemaObjC::handleBlocksAttr(Decl *D, const ParsedAttr &AL) { + if (!AL.isArgIdent(0)) { + Diag(AL.getLoc(), diag::err_attribute_argument_n_type) + << AL << 1 << AANT_ArgumentIdentifier; + return; + } + + IdentifierInfo *II = AL.getArgAsIdent(0)->Ident; + BlocksAttr::BlockType type; + if (!BlocksAttr::ConvertStrToBlockType(II->getName(), type)) { + Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) << AL << II; + return; + } + + D->addAttr(::new (getASTContext()) BlocksAttr(getASTContext(), AL, type)); +} + +static bool isValidSubjectOfNSReturnsRetainedAttribute(QualType QT) { + return QT->isDependentType() || QT->isObjCRetainableType(); +} + +static bool isValidSubjectOfNSAttribute(QualType QT) { + return QT->isDependentType() || QT->isObjCObjectPointerType() || + QT->isObjCNSObjectType(); +} + +static bool isValidSubjectOfCFAttribute(QualType QT) { + return QT->isDependentType() || QT->isPointerType() || + isValidSubjectOfNSAttribute(QT); +} + +static bool isValidSubjectOfOSAttribute(QualType QT) { + if (QT->isDependentType()) + return true; + QualType PT = QT->getPointeeType(); + return !PT.isNull() && PT->getAsCXXRecordDecl() != nullptr; +} + +void SemaObjC::AddXConsumedAttr(Decl *D, const AttributeCommonInfo &CI, + Sema::RetainOwnershipKind K, + bool IsTemplateInstantiation) { + ValueDecl *VD = cast<ValueDecl>(D); + switch (K) { + case Sema::RetainOwnershipKind::OS: + handleSimpleAttributeOrDiagnose<OSConsumedAttr>( + *this, VD, CI, isValidSubjectOfOSAttribute(VD->getType()), + diag::warn_ns_attribute_wrong_parameter_type, + /*ExtraArgs=*/CI.getRange(), "os_consumed", /*pointers*/ 1); + return; + case Sema::RetainOwnershipKind::NS: + handleSimpleAttributeOrDiagnose<NSConsumedAttr>( + *this, VD, CI, isValidSubjectOfNSAttribute(VD->getType()), + + // These attributes are normally just advisory, but in ARC, ns_consumed + // is significant. Allow non-dependent code to contain inappropriate + // attributes even in ARC, but require template instantiations to be + // set up correctly. + ((IsTemplateInstantiation && getLangOpts().ObjCAutoRefCount) + ? diag::err_ns_attribute_wrong_parameter_type + : diag::warn_ns_attribute_wrong_parameter_type), + /*ExtraArgs=*/CI.getRange(), "ns_consumed", /*objc pointers*/ 0); + return; + case Sema::RetainOwnershipKind::CF: + handleSimpleAttributeOrDiagnose<CFConsumedAttr>( + *this, VD, CI, isValidSubjectOfCFAttribute(VD->getType()), + diag::warn_ns_attribute_wrong_parameter_type, + /*ExtraArgs=*/CI.getRange(), "cf_consumed", /*pointers*/ 1); + return; + } +} + +Sema::RetainOwnershipKind +SemaObjC::parsedAttrToRetainOwnershipKind(const ParsedAttr &AL) { + switch (AL.getKind()) { + case ParsedAttr::AT_CFConsumed: + case ParsedAttr::AT_CFReturnsRetained: + case ParsedAttr::AT_CFReturnsNotRetained: + return Sema::RetainOwnershipKind::CF; + case ParsedAttr::AT_OSConsumesThis: + case ParsedAttr::AT_OSConsumed: + case ParsedAttr::AT_OSReturnsRetained: + case ParsedAttr::AT_OSReturnsNotRetained: + case ParsedAttr::AT_OSReturnsRetainedOnZero: + case ParsedAttr::AT_OSReturnsRetainedOnNonZero: + return Sema::RetainOwnershipKind::OS; + case ParsedAttr::AT_NSConsumesSelf: + case ParsedAttr::AT_NSConsumed: + case ParsedAttr::AT_NSReturnsRetained: + case ParsedAttr::AT_NSReturnsNotRetained: + case ParsedAttr::AT_NSReturnsAutoreleased: + return Sema::RetainOwnershipKind::NS; + default: + llvm_unreachable("Wrong argument supplied"); + } +} + +bool SemaObjC::checkNSReturnsRetainedReturnType(SourceLocation Loc, + QualType QT) { + if (isValidSubjectOfNSReturnsRetainedAttribute(QT)) + return false; + + Diag(Loc, diag::warn_ns_attribute_wrong_return_type) + << "'ns_returns_retained'" << 0 << 0; + return true; +} + +/// \return whether the parameter is a pointer to OSObject pointer. +bool SemaObjC::isValidOSObjectOutParameter(const Decl *D) { + const auto *PVD = dyn_cast<ParmVarDecl>(D); + if (!PVD) + return false; + QualType QT = PVD->getType(); + QualType PT = QT->getPointeeType(); + return !PT.isNull() && isValidSubjectOfOSAttribute(PT); +} + +void SemaObjC::handleXReturnsXRetainedAttr(Decl *D, const ParsedAttr &AL) { + QualType ReturnType; + Sema::RetainOwnershipKind K = parsedAttrToRetainOwnershipKind(AL); + + if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { + ReturnType = MD->getReturnType(); + } else if (getLangOpts().ObjCAutoRefCount && hasDeclarator(D) && + (AL.getKind() == ParsedAttr::AT_NSReturnsRetained)) { + return; // ignore: was handled as a type attribute + } else if (const auto *PD = dyn_cast<ObjCPropertyDecl>(D)) { + ReturnType = PD->getType(); + } else if (const auto *FD = dyn_cast<FunctionDecl>(D)) { + ReturnType = FD->getReturnType(); + } else if (const auto *Param = dyn_cast<ParmVarDecl>(D)) { + // Attributes on parameters are used for out-parameters, + // passed as pointers-to-pointers. + unsigned DiagID = K == Sema::RetainOwnershipKind::CF + ? /*pointer-to-CF-pointer*/ 2 + : /*pointer-to-OSObject-pointer*/ 3; + ReturnType = Param->getType()->getPointeeType(); + if (ReturnType.isNull()) { + Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_parameter_type) + << AL << DiagID << AL.getRange(); + return; + } + } else if (AL.isUsedAsTypeAttr()) { + return; + } else { + AttributeDeclKind ExpectedDeclKind; + switch (AL.getKind()) { + default: + llvm_unreachable("invalid ownership attribute"); + case ParsedAttr::AT_NSReturnsRetained: + case ParsedAttr::AT_NSReturnsAutoreleased: + case ParsedAttr::AT_NSReturnsNotRetained: + ExpectedDeclKind = ExpectedFunctionOrMethod; + break; + + case ParsedAttr::AT_OSReturnsRetained: + case ParsedAttr::AT_OSReturnsNotRetained: + case ParsedAttr::AT_CFReturnsRetained: + case ParsedAttr::AT_CFReturnsNotRetained: + ExpectedDeclKind = ExpectedFunctionMethodOrParameter; + break; + } + Diag(D->getBeginLoc(), diag::warn_attribute_wrong_decl_type) + << AL.getRange() << AL << AL.isRegularKeywordAttribute() + << ExpectedDeclKind; + return; + } + + bool TypeOK; + bool Cf; + unsigned ParmDiagID = 2; // Pointer-to-CF-pointer + switch (AL.getKind()) { + default: + llvm_unreachable("invalid ownership attribute"); + case ParsedAttr::AT_NSReturnsRetained: + TypeOK = isValidSubjectOfNSReturnsRetainedAttribute(ReturnType); + Cf = false; + break; + + case ParsedAttr::AT_NSReturnsAutoreleased: + case ParsedAttr::AT_NSReturnsNotRetained: + TypeOK = isValidSubjectOfNSAttribute(ReturnType); + Cf = false; + break; + + case ParsedAttr::AT_CFReturnsRetained: + case ParsedAttr::AT_CFReturnsNotRetained: + TypeOK = isValidSubjectOfCFAttribute(ReturnType); + Cf = true; + break; + + case ParsedAttr::AT_OSReturnsRetained: + case ParsedAttr::AT_OSReturnsNotRetained: + TypeOK = isValidSubjectOfOSAttribute(ReturnType); + Cf = true; + ParmDiagID = 3; // Pointer-to-OSObject-pointer + break; + } + + if (!TypeOK) { + if (AL.isUsedAsTypeAttr()) + return; + + if (isa<ParmVarDecl>(D)) { + Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_parameter_type) + << AL << ParmDiagID << AL.getRange(); + } else { + // Needs to be kept in sync with warn_ns_attribute_wrong_return_type. + enum : unsigned { Function, Method, Property } SubjectKind = Function; + if (isa<ObjCMethodDecl>(D)) + SubjectKind = Method; + else if (isa<ObjCPropertyDecl>(D)) + SubjectKind = Property; + Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_return_type) + << AL << SubjectKind << Cf << AL.getRange(); + } + return; + } + + switch (AL.getKind()) { + default: + llvm_unreachable("invalid ownership attribute"); + case ParsedAttr::AT_NSReturnsAutoreleased: + handleSimpleAttribute<NSReturnsAutoreleasedAttr>(*this, D, AL); + return; + case ParsedAttr::AT_CFReturnsNotRetained: + handleSimpleAttribute<CFReturnsNotRetainedAttr>(*this, D, AL); + return; + case ParsedAttr::AT_NSReturnsNotRetained: + handleSimpleAttribute<NSReturnsNotRetainedAttr>(*this, D, AL); + return; + case ParsedAttr::AT_CFReturnsRetained: + handleSimpleAttribute<CFReturnsRetainedAttr>(*this, D, AL); + return; + case ParsedAttr::AT_NSReturnsRetained: + handleSimpleAttribute<NSReturnsRetainedAttr>(*this, D, AL); + return; + case ParsedAttr::AT_OSReturnsRetained: + handleSimpleAttribute<OSReturnsRetainedAttr>(*this, D, AL); + return; + case ParsedAttr::AT_OSReturnsNotRetained: + handleSimpleAttribute<OSReturnsNotRetainedAttr>(*this, D, AL); + return; + }; +} + +void SemaObjC::handleReturnsInnerPointerAttr(Decl *D, const ParsedAttr &Attrs) { + const int EP_ObjCMethod = 1; + const int EP_ObjCProperty = 2; + + SourceLocation loc = Attrs.getLoc(); + QualType resultType; + if (isa<ObjCMethodDecl>(D)) + resultType = cast<ObjCMethodDecl>(D)->getReturnType(); + else + resultType = cast<ObjCPropertyDecl>(D)->getType(); + + if (!resultType->isReferenceType() && + (!resultType->isPointerType() || resultType->isObjCRetainableType())) { + Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_return_type) + << SourceRange(loc) << Attrs + << (isa<ObjCMethodDecl>(D) ? EP_ObjCMethod : EP_ObjCProperty) + << /*non-retainable pointer*/ 2; + + // Drop the attribute. + return; + } + + D->addAttr(::new (getASTContext()) + ObjCReturnsInnerPointerAttr(getASTContext(), Attrs)); +} + +void SemaObjC::handleRequiresSuperAttr(Decl *D, const ParsedAttr &Attrs) { + const auto *Method = cast<ObjCMethodDecl>(D); + + const DeclContext *DC = Method->getDeclContext(); + if (const auto *PDecl = dyn_cast_if_present<ObjCProtocolDecl>(DC)) { + Diag(D->getBeginLoc(), diag::warn_objc_requires_super_protocol) + << Attrs << 0; + Diag(PDecl->getLocation(), diag::note_protocol_decl); + return; + } + if (Method->getMethodFamily() == OMF_dealloc) { + Diag(D->getBeginLoc(), diag::warn_objc_requires_super_protocol) + << Attrs << 1; + return; + } + + D->addAttr(::new (getASTContext()) + ObjCRequiresSuperAttr(getASTContext(), Attrs)); +} + +void SemaObjC::handleNSErrorDomain(Decl *D, const ParsedAttr &Attr) { + if (!isa<TagDecl>(D)) { + Diag(D->getBeginLoc(), diag::err_nserrordomain_invalid_decl) << 0; + return; + } + + IdentifierLoc *IdentLoc = + Attr.isArgIdent(0) ? Attr.getArgAsIdent(0) : nullptr; + if (!IdentLoc || !IdentLoc->Ident) { + // Try to locate the argument directly. + SourceLocation Loc = Attr.getLoc(); + if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0)) + Loc = Attr.getArgAsExpr(0)->getBeginLoc(); + + Diag(Loc, diag::err_nserrordomain_invalid_decl) << 0; + return; + } + + // Verify that the identifier is a valid decl in the C decl namespace. + LookupResult Result(SemaRef, DeclarationName(IdentLoc->Ident), + SourceLocation(), + Sema::LookupNameKind::LookupOrdinaryName); + if (!SemaRef.LookupName(Result, SemaRef.TUScope) || + !Result.getAsSingle<VarDecl>()) { + Diag(IdentLoc->Loc, diag::err_nserrordomain_invalid_decl) + << 1 << IdentLoc->Ident; + return; + } + + D->addAttr(::new (getASTContext()) + NSErrorDomainAttr(getASTContext(), Attr, IdentLoc->Ident)); +} + +void SemaObjC::handleBridgeAttr(Decl *D, const ParsedAttr &AL) { + IdentifierLoc *Parm = AL.isArgIdent(0) ? AL.getArgAsIdent(0) : nullptr; + + if (!Parm) { + Diag(D->getBeginLoc(), diag::err_objc_attr_not_id) << AL << 0; + return; + } + + // Typedefs only allow objc_bridge(id) and have some additional checking. + if (const auto *TD = dyn_cast<TypedefNameDecl>(D)) { + if (!Parm->Ident->isStr("id")) { + Diag(AL.getLoc(), diag::err_objc_attr_typedef_not_id) << AL; + return; + } + + // Only allow 'cv void *'. + QualType T = TD->getUnderlyingType(); + if (!T->isVoidPointerType()) { + Diag(AL.getLoc(), diag::err_objc_attr_typedef_not_void_pointer); + return; + } + } + + D->addAttr(::new (getASTContext()) + ObjCBridgeAttr(getASTContext(), AL, Parm->Ident)); +} + +void SemaObjC::handleBridgeMutableAttr(Decl *D, const ParsedAttr &AL) { + IdentifierLoc *Parm = AL.isArgIdent(0) ? AL.getArgAsIdent(0) : nullptr; + + if (!Parm) { + Diag(D->getBeginLoc(), diag::err_objc_attr_not_id) << AL << 0; + return; + } + + D->addAttr(::new (getASTContext()) + ObjCBridgeMutableAttr(getASTContext(), AL, Parm->Ident)); +} + +void SemaObjC::handleBridgeRelatedAttr(Decl *D, const ParsedAttr &AL) { + IdentifierInfo *RelatedClass = + AL.isArgIdent(0) ? AL.getArgAsIdent(0)->Ident : nullptr; + if (!RelatedClass) { + Diag(D->getBeginLoc(), diag::err_objc_attr_not_id) << AL << 0; + return; + } + IdentifierInfo *ClassMethod = + AL.getArgAsIdent(1) ? AL.getArgAsIdent(1)->Ident : nullptr; + IdentifierInfo *InstanceMethod = + AL.getArgAsIdent(2) ? AL.getArgAsIdent(2)->Ident : nullptr; + D->addAttr(::new (getASTContext()) ObjCBridgeRelatedAttr( + getASTContext(), AL, RelatedClass, ClassMethod, InstanceMethod)); +} + +void SemaObjC::handleDesignatedInitializer(Decl *D, const ParsedAttr &AL) { + DeclContext *Ctx = D->getDeclContext(); + + // This attribute can only be applied to methods in interfaces or class + // extensions. + if (!isa<ObjCInterfaceDecl>(Ctx) && + !(isa<ObjCCategoryDecl>(Ctx) && + cast<ObjCCategoryDecl>(Ctx)->IsClassExtension())) { + Diag(D->getLocation(), diag::err_designated_init_attr_non_init); + return; + } + + ObjCInterfaceDecl *IFace; + if (auto *CatDecl = dyn_cast<ObjCCategoryDecl>(Ctx)) + IFace = CatDecl->getClassInterface(); + else + IFace = cast<ObjCInterfaceDecl>(Ctx); + + if (!IFace) + return; + + IFace->setHasDesignatedInitializers(); + D->addAttr(::new (getASTContext()) + ObjCDesignatedInitializerAttr(getASTContext(), AL)); +} + +void SemaObjC::handleRuntimeName(Decl *D, const ParsedAttr &AL) { + StringRef MetaDataName; + if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, MetaDataName)) + return; + D->addAttr(::new (getASTContext()) + ObjCRuntimeNameAttr(getASTContext(), AL, MetaDataName)); +} + +// When a user wants to use objc_boxable with a union or struct +// but they don't have access to the declaration (legacy/third-party code) +// then they can 'enable' this feature with a typedef: +// typedef struct __attribute((objc_boxable)) legacy_struct legacy_struct; +void SemaObjC::handleBoxable(Decl *D, const ParsedAttr &AL) { + bool notify = false; + + auto *RD = dyn_cast<RecordDecl>(D); + if (RD && RD->getDefinition()) { + RD = RD->getDefinition(); + notify = true; + } + + if (RD) { + ObjCBoxableAttr *BoxableAttr = + ::new (getASTContext()) ObjCBoxableAttr(getASTContext(), AL); + RD->addAttr(BoxableAttr); + if (notify) { + // we need to notify ASTReader/ASTWriter about + // modification of existing declaration + if (ASTMutationListener *L = SemaRef.getASTMutationListener()) + L->AddedAttributeToRecord(BoxableAttr, RD); + } + } +} + +void SemaObjC::handleOwnershipAttr(Decl *D, const ParsedAttr &AL) { + if (hasDeclarator(D)) + return; + + Diag(D->getBeginLoc(), diag::err_attribute_wrong_decl_type) + << AL.getRange() << AL << AL.isRegularKeywordAttribute() + << ExpectedVariable; +} + +void SemaObjC::handlePreciseLifetimeAttr(Decl *D, const ParsedAttr &AL) { + const auto *VD = cast<ValueDecl>(D); + QualType QT = VD->getType(); + + if (!QT->isDependentType() && !QT->isObjCLifetimeType()) { + Diag(AL.getLoc(), diag::err_objc_precise_lifetime_bad_type) << QT; + return; + } + + Qualifiers::ObjCLifetime Lifetime = QT.getObjCLifetime(); + + // If we have no lifetime yet, check the lifetime we're presumably + // going to infer. + if (Lifetime == Qualifiers::OCL_None && !QT->isDependentType()) + Lifetime = QT->getObjCARCImplicitLifetime(); + + switch (Lifetime) { + case Qualifiers::OCL_None: + assert(QT->isDependentType() && + "didn't infer lifetime for non-dependent type?"); + break; + + case Qualifiers::OCL_Weak: // meaningful + case Qualifiers::OCL_Strong: // meaningful + break; + + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + Diag(AL.getLoc(), diag::warn_objc_precise_lifetime_meaningless) + << (Lifetime == Qualifiers::OCL_Autoreleasing); + break; + } + + D->addAttr(::new (getASTContext()) + ObjCPreciseLifetimeAttr(getASTContext(), AL)); +} + +static bool tryMakeVariablePseudoStrong(Sema &S, VarDecl *VD, + bool DiagnoseFailure) { + QualType Ty = VD->getType(); + if (!Ty->isObjCRetainableType()) { + if (DiagnoseFailure) { + S.Diag(VD->getBeginLoc(), diag::warn_ignored_objc_externally_retained) + << 0; + } + return false; + } + + Qualifiers::ObjCLifetime LifetimeQual = Ty.getQualifiers().getObjCLifetime(); + + // SemaObjC::inferObjCARCLifetime must run after processing decl attributes + // (because __block lowers to an attribute), so if the lifetime hasn't been + // explicitly specified, infer it locally now. + if (LifetimeQual == Qualifiers::OCL_None) + LifetimeQual = Ty->getObjCARCImplicitLifetime(); + + // The attributes only really makes sense for __strong variables; ignore any + // attempts to annotate a parameter with any other lifetime qualifier. + if (LifetimeQual != Qualifiers::OCL_Strong) { + if (DiagnoseFailure) { + S.Diag(VD->getBeginLoc(), diag::warn_ignored_objc_externally_retained) + << 1; + } + return false; + } + + // Tampering with the type of a VarDecl here is a bit of a hack, but we need + // to ensure that the variable is 'const' so that we can error on + // modification, which can otherwise over-release. + VD->setType(Ty.withConst()); + VD->setARCPseudoStrong(true); + return true; +} + +void SemaObjC::handleExternallyRetainedAttr(Decl *D, const ParsedAttr &AL) { + if (auto *VD = dyn_cast<VarDecl>(D)) { + assert(!isa<ParmVarDecl>(VD) && "should be diagnosed automatically"); + if (!VD->hasLocalStorage()) { + Diag(D->getBeginLoc(), diag::warn_ignored_objc_externally_retained) << 0; + return; + } + + if (!tryMakeVariablePseudoStrong(SemaRef, VD, /*DiagnoseFailure=*/true)) + return; + + handleSimpleAttribute<ObjCExternallyRetainedAttr>(*this, D, AL); + return; + } + + // If D is a function-like declaration (method, block, or function), then we + // make every parameter psuedo-strong. + unsigned NumParams = + hasFunctionProto(D) ? getFunctionOrMethodNumParams(D) : 0; + for (unsigned I = 0; I != NumParams; ++I) { + auto *PVD = const_cast<ParmVarDecl *>(getFunctionOrMethodParam(D, I)); + QualType Ty = PVD->getType(); + + // If a user wrote a parameter with __strong explicitly, then assume they + // want "real" strong semantics for that parameter. This works because if + // the parameter was written with __strong, then the strong qualifier will + // be non-local. + if (Ty.getLocalUnqualifiedType().getQualifiers().getObjCLifetime() == + Qualifiers::OCL_Strong) + continue; + + tryMakeVariablePseudoStrong(SemaRef, PVD, /*DiagnoseFailure=*/false); + } + handleSimpleAttribute<ObjCExternallyRetainedAttr>(*this, D, AL); +} + +bool SemaObjC::GetFormatNSStringIdx(const FormatAttr *Format, unsigned &Idx) { + Sema::FormatStringInfo FSI; + if ((SemaRef.GetFormatStringType(Format) == Sema::FST_NSString) && + SemaRef.getFormatStringInfo(Format, false, true, &FSI)) { + Idx = FSI.FormatIdx; + return true; + } + return false; +} + +/// Diagnose use of %s directive in an NSString which is being passed +/// as formatting string to formatting method. +void SemaObjC::DiagnoseCStringFormatDirectiveInCFAPI(const NamedDecl *FDecl, + Expr **Args, + unsigned NumArgs) { + unsigned Idx = 0; + bool Format = false; + ObjCStringFormatFamily SFFamily = FDecl->getObjCFStringFormattingFamily(); + if (SFFamily == ObjCStringFormatFamily::SFF_CFString) { + Idx = 2; + Format = true; + } else + for (const auto *I : FDecl->specific_attrs<FormatAttr>()) { + if (GetFormatNSStringIdx(I, Idx)) { + Format = true; + break; + } + } + if (!Format || NumArgs <= Idx) + return; + const Expr *FormatExpr = Args[Idx]; + if (const CStyleCastExpr *CSCE = dyn_cast<CStyleCastExpr>(FormatExpr)) + FormatExpr = CSCE->getSubExpr(); + const StringLiteral *FormatString; + if (const ObjCStringLiteral *OSL = + dyn_cast<ObjCStringLiteral>(FormatExpr->IgnoreParenImpCasts())) + FormatString = OSL->getString(); + else + FormatString = dyn_cast<StringLiteral>(FormatExpr->IgnoreParenImpCasts()); + if (!FormatString) + return; + if (SemaRef.FormatStringHasSArg(FormatString)) { + Diag(FormatExpr->getExprLoc(), diag::warn_objc_cdirective_format_string) + << "%s" << 1 << 1; + Diag(FDecl->getLocation(), diag::note_entity_declared_at) + << FDecl->getDeclName(); + } +} + +bool SemaObjC::isSignedCharBool(QualType Ty) { + return Ty->isSpecificBuiltinType(BuiltinType::SChar) && getLangOpts().ObjC && + NSAPIObj->isObjCBOOLType(Ty); +} + +void SemaObjC::adornBoolConversionDiagWithTernaryFixit( + Expr *SourceExpr, const Sema::SemaDiagnosticBuilder &Builder) { + Expr *Ignored = SourceExpr->IgnoreImplicit(); + if (const auto *OVE = dyn_cast<OpaqueValueExpr>(Ignored)) + Ignored = OVE->getSourceExpr(); + bool NeedsParens = isa<AbstractConditionalOperator>(Ignored) || + isa<BinaryOperator>(Ignored) || + isa<CXXOperatorCallExpr>(Ignored); + SourceLocation EndLoc = SemaRef.getLocForEndOfToken(SourceExpr->getEndLoc()); + if (NeedsParens) + Builder << FixItHint::CreateInsertion(SourceExpr->getBeginLoc(), "(") + << FixItHint::CreateInsertion(EndLoc, ")"); + Builder << FixItHint::CreateInsertion(EndLoc, " ? YES : NO"); +} + +/// Check a single element within a collection literal against the +/// target element type. +static void checkCollectionLiteralElement(Sema &S, QualType TargetElementType, + Expr *Element, unsigned ElementKind) { + // Skip a bitcast to 'id' or qualified 'id'. + if (auto ICE = dyn_cast<ImplicitCastExpr>(Element)) { + if (ICE->getCastKind() == CK_BitCast && + ICE->getSubExpr()->getType()->getAs<ObjCObjectPointerType>()) + Element = ICE->getSubExpr(); + } + + QualType ElementType = Element->getType(); + ExprResult ElementResult(Element); + if (ElementType->getAs<ObjCObjectPointerType>() && + S.CheckSingleAssignmentConstraints(TargetElementType, ElementResult, + false, false) != Sema::Compatible) { + S.Diag(Element->getBeginLoc(), diag::warn_objc_collection_literal_element) + << ElementType << ElementKind << TargetElementType + << Element->getSourceRange(); + } + + if (auto ArrayLiteral = dyn_cast<ObjCArrayLiteral>(Element)) + S.ObjC().checkArrayLiteral(TargetElementType, ArrayLiteral); + else if (auto DictionaryLiteral = dyn_cast<ObjCDictionaryLiteral>(Element)) + S.ObjC().checkDictionaryLiteral(TargetElementType, DictionaryLiteral); +} + +/// Check an Objective-C array literal being converted to the given +/// target type. +void SemaObjC::checkArrayLiteral(QualType TargetType, + ObjCArrayLiteral *ArrayLiteral) { + if (!NSArrayDecl) + return; + + const auto *TargetObjCPtr = TargetType->getAs<ObjCObjectPointerType>(); + if (!TargetObjCPtr) + return; + + if (TargetObjCPtr->isUnspecialized() || + TargetObjCPtr->getInterfaceDecl()->getCanonicalDecl() != + NSArrayDecl->getCanonicalDecl()) + return; + + auto TypeArgs = TargetObjCPtr->getTypeArgs(); + if (TypeArgs.size() != 1) + return; + + QualType TargetElementType = TypeArgs[0]; + for (unsigned I = 0, N = ArrayLiteral->getNumElements(); I != N; ++I) { + checkCollectionLiteralElement(SemaRef, TargetElementType, + ArrayLiteral->getElement(I), 0); + } +} + +void SemaObjC::checkDictionaryLiteral( + QualType TargetType, ObjCDictionaryLiteral *DictionaryLiteral) { + if (!NSDictionaryDecl) + return; + + const auto *TargetObjCPtr = TargetType->getAs<ObjCObjectPointerType>(); + if (!TargetObjCPtr) + return; + + if (TargetObjCPtr->isUnspecialized() || + TargetObjCPtr->getInterfaceDecl()->getCanonicalDecl() != + NSDictionaryDecl->getCanonicalDecl()) + return; + + auto TypeArgs = TargetObjCPtr->getTypeArgs(); + if (TypeArgs.size() != 2) + return; + + QualType TargetKeyType = TypeArgs[0]; + QualType TargetObjectType = TypeArgs[1]; + for (unsigned I = 0, N = DictionaryLiteral->getNumElements(); I != N; ++I) { + auto Element = DictionaryLiteral->getKeyValueElement(I); + checkCollectionLiteralElement(SemaRef, TargetKeyType, Element.Key, 1); + checkCollectionLiteralElement(SemaRef, TargetObjectType, Element.Value, 2); + } +} + +} // namespace clang |