//===----- 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(First)) { if (!DS->isSingleDecl()) return StmtError(Diag((*DS->decl_begin())->getLocation(), diag::err_toomany_element_decls)); VarDecl *D = dyn_cast(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(First); if (!FirstE->isTypeDependent() && !FirstE->isLValue()) return StmtError( Diag(First->getBeginLoc(), diag::err_selector_element_not_lvalue) << First->getSourceRange()); FirstType = static_cast(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(); 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(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(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(); 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(); 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 protocols, ArrayRef protocolLocs, SourceLocation rAngleLoc) { ASTContext &Context = getASTContext(); // Form id. 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(); ObjCObjectPointerTL.setStarLoc(SourceLocation()); // implicit auto ObjCObjectTL = ObjCObjectPointerTL.getPointeeLoc().castAs(); 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 TypeArgs, SourceLocation TypeArgsRAngleLoc, SourceLocation ProtocolLAngleLoc, ArrayRef Protocols, ArrayRef 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 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 or Class, we'll have an // object pointer type. Fill in source information for it. if (auto ObjCObjectPointerTL = ResultTL.getAs()) { // The '*' is implicit. ObjCObjectPointerTL.setStarLoc(SourceLocation()); ResultTL = ObjCObjectPointerTL.getPointeeLoc(); } if (auto OTPTL = ResultTL.getAs()) { // 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(); // 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 Protocols, ArrayRef 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 typeArgs, SourceRange typeArgsRange, bool failOnError, bool rebuilding) { // We can only apply type arguments to an Objective-C class type. const auto *objcObjectType = type->getAs(); 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 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()) { 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()) 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()) { // 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(); // 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 TypeArgs, SourceLocation TypeArgsRAngleLoc, SourceLocation ProtocolLAngleLoc, ArrayRef Protocols, ArrayRef 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(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(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(e)) { VarDecl *var = dyn_cast(ref->getDecl()); if (!var) return false; return considerVariable(var, ref, owner); } if (MemberExpr *member = dyn_cast(e)) { if (member->isArrow()) return false; // Don't count this as an indirect ownership. e = member->getBase(); continue; } if (PseudoObjectExpr *pseudo = dyn_cast(e)) { // Only pay attention to pseudo-objects on property references. ObjCPropertyRefExpr *pre = dyn_cast( 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( cast(pre->getBase())->getSourceExpr()); continue; } // Array ivars? return false; } } namespace { struct FindCaptureVisitor : EvaluatedExprVisitor { VarDecl *Variable; Expr *Capturer = nullptr; bool VarWillBeReased = false; FindCaptureVisitor(ASTContext &Context, VarDecl *variable) : EvaluatedExprVisitor(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(LHS)) { if (DRE->getDecl() != Variable) return; if (Expr *RHS = BinOp->getRHS()) { RHS = RHS->IgnoreParenCasts(); std::optional 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(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(e)) { if (CE->getNumArgs() == 1) { FunctionDecl *Fn = dyn_cast_or_null(CE->getCalleeDecl()); if (Fn) { const IdentifierInfo *FnI = Fn->getIdentifier(); if (FnI && FnI->isStr("_Block_copy")) { e = CE->getArg(0)->IgnoreParenCasts(); } } } } BlockExpr *block = dyn_cast(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 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 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 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 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 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 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 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(Arg)) { Arg = OE->getSourceExpr()->IgnoreImpCasts(); } if (Message->getReceiverKind() == ObjCMessageExpr::SuperInstance) { if (DeclRefExpr *ArgRE = dyn_cast(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(Receiver)) { Receiver = OE->getSourceExpr()->IgnoreImpCasts(); } if (DeclRefExpr *ReceiverRE = dyn_cast(Receiver)) { if (DeclRefExpr *ArgRE = dyn_cast(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(Receiver)) { if (ObjCIvarRefExpr *IvarArgRE = dyn_cast(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()) 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(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 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 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(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(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()) 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()) 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(); 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(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 ConversionDecls; for (NamedDecl *D : cast(RecordTy->getDecl()) ->getVisibleConversionFunctions()) { if (CXXConversionDecl *Conversion = dyn_cast(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() || D->hasAttr()) 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()) bridgedType = bridgeAttr->getBridgedType(); else if (auto bridgeAttr = RD->getAttr()) bridgedType = bridgeAttr->getBridgedType(); if (bridgedType == getNSErrorIdent()) { CFError = RD; return true; } } return false; } bool SemaObjC::isNSStringType(QualType T, bool AllowNSAttributedString) { const auto *PT = T->getAs(); 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(); if (!PT) return false; const auto *RT = PT->getPointeeType()->getAs(); 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(D)) { if (!VD->getType()->getAs()) { S.Diag(AL.getLoc(), diag::warn_iboutlet_object_type) << AL << VD->getType() << 0; return false; } } else if (const auto *PD = dyn_cast(D)) { if (!PD->getType()->getAs()) { 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(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(D->getDeclContext())) { Diag(AL.getLoc(), diag::err_objc_direct_on_protocol) << false; return; } if (getLangOpts().ObjCRuntime.allowsDirectDispatch()) { handleSimpleAttribute(*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(*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(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(D)) { QualType T = TD->getUnderlyingType(); if (!T->isCARCBridgableType()) { Diag(TD->getLocation(), diag::err_nsobject_attribute); return; } } else if (const auto *PD = dyn_cast(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(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(D); switch (K) { case Sema::RetainOwnershipKind::OS: handleSimpleAttributeOrDiagnose( *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( *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( *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(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(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(D)) { ReturnType = PD->getType(); } else if (const auto *FD = dyn_cast(D)) { ReturnType = FD->getReturnType(); } else if (const auto *Param = dyn_cast(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(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(D)) SubjectKind = Method; else if (isa(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(*this, D, AL); return; case ParsedAttr::AT_CFReturnsNotRetained: handleSimpleAttribute(*this, D, AL); return; case ParsedAttr::AT_NSReturnsNotRetained: handleSimpleAttribute(*this, D, AL); return; case ParsedAttr::AT_CFReturnsRetained: handleSimpleAttribute(*this, D, AL); return; case ParsedAttr::AT_NSReturnsRetained: handleSimpleAttribute(*this, D, AL); return; case ParsedAttr::AT_OSReturnsRetained: handleSimpleAttribute(*this, D, AL); return; case ParsedAttr::AT_OSReturnsNotRetained: handleSimpleAttribute(*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(D)) resultType = cast(D)->getReturnType(); else resultType = cast(D)->getType(); if (!resultType->isReferenceType() && (!resultType->isPointerType() || resultType->isObjCRetainableType())) { Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_return_type) << SourceRange(loc) << Attrs << (isa(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(D); const DeclContext *DC = Method->getDeclContext(); if (const auto *PDecl = dyn_cast_if_present(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(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()) { 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(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(Ctx) && !(isa(Ctx) && cast(Ctx)->IsClassExtension())) { Diag(D->getLocation(), diag::err_designated_init_attr_non_init); return; } ObjCInterfaceDecl *IFace; if (auto *CatDecl = dyn_cast(Ctx)) IFace = CatDecl->getClassInterface(); else IFace = cast(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(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(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(D)) { assert(!isa(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(*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(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(*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()) { if (GetFormatNSStringIdx(I, Idx)) { Format = true; break; } } if (!Format || NumArgs <= Idx) return; const Expr *FormatExpr = Args[Idx]; if (const CStyleCastExpr *CSCE = dyn_cast(FormatExpr)) FormatExpr = CSCE->getSubExpr(); const StringLiteral *FormatString; if (const ObjCStringLiteral *OSL = dyn_cast(FormatExpr->IgnoreParenImpCasts())) FormatString = OSL->getString(); else FormatString = dyn_cast(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(Ignored)) Ignored = OVE->getSourceExpr(); bool NeedsParens = isa(Ignored) || isa(Ignored) || isa(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(Element)) { if (ICE->getCastKind() == CK_BitCast && ICE->getSubExpr()->getType()->getAs()) Element = ICE->getSubExpr(); } QualType ElementType = Element->getType(); ExprResult ElementResult(Element); if (ElementType->getAs() && 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(Element)) S.ObjC().checkArrayLiteral(TargetElementType, ArrayLiteral); else if (auto DictionaryLiteral = dyn_cast(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(); 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(); 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