diff options
author | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
---|---|---|
committer | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
commit | 6ffe9e53658409f212834330e13564e4952558f6 (patch) | |
tree | 85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/clang14/lib/Sema/SemaExprObjC.cpp | |
parent | 726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff) | |
download | ydb-6ffe9e53658409f212834330e13564e4952558f6.tar.gz |
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/clang14/lib/Sema/SemaExprObjC.cpp')
-rw-r--r-- | contrib/libs/clang14/lib/Sema/SemaExprObjC.cpp | 4766 |
1 files changed, 4766 insertions, 0 deletions
diff --git a/contrib/libs/clang14/lib/Sema/SemaExprObjC.cpp b/contrib/libs/clang14/lib/Sema/SemaExprObjC.cpp new file mode 100644 index 0000000000..4702c405fb --- /dev/null +++ b/contrib/libs/clang14/lib/Sema/SemaExprObjC.cpp @@ -0,0 +1,4766 @@ +//===--- SemaExprObjC.cpp - Semantic Analysis for ObjC Expressions --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements semantic analysis for Objective-C expressions. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeLoc.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/Basic/Builtins.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/Rewriters.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/Initialization.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Scope.h" +#include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/SemaInternal.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ConvertUTF.h" + +using namespace clang; +using namespace sema; +using llvm::makeArrayRef; + +ExprResult Sema::ParseObjCStringLiteral(SourceLocation *AtLocs, + ArrayRef<Expr *> Strings) { + // Most ObjC strings are formed out of a single piece. However, we *can* + // have strings formed out of multiple @ strings with multiple pptokens in + // each one, e.g. @"foo" "bar" @"baz" "qux" which need to be turned into one + // StringLiteral for ObjCStringLiteral to hold onto. + StringLiteral *S = cast<StringLiteral>(Strings[0]); + + // If we have a multi-part string, merge it all together. + if (Strings.size() != 1) { + // Concatenate objc strings. + SmallString<128> StrBuf; + SmallVector<SourceLocation, 8> StrLocs; + + for (Expr *E : Strings) { + S = cast<StringLiteral>(E); + + // ObjC strings can't be wide or UTF. + if (!S->isAscii()) { + Diag(S->getBeginLoc(), diag::err_cfstring_literal_not_string_constant) + << S->getSourceRange(); + return true; + } + + // Append the string. + StrBuf += S->getString(); + + // Get the locations of the string tokens. + StrLocs.append(S->tokloc_begin(), S->tokloc_end()); + } + + // Create the aggregate string with the appropriate content and location + // information. + const ConstantArrayType *CAT = Context.getAsConstantArrayType(S->getType()); + assert(CAT && "String literal not of constant array type!"); + QualType StrTy = Context.getConstantArrayType( + CAT->getElementType(), llvm::APInt(32, StrBuf.size() + 1), nullptr, + CAT->getSizeModifier(), CAT->getIndexTypeCVRQualifiers()); + S = StringLiteral::Create(Context, StrBuf, StringLiteral::Ascii, + /*Pascal=*/false, StrTy, &StrLocs[0], + StrLocs.size()); + } + + return BuildObjCStringLiteral(AtLocs[0], S); +} + +ExprResult Sema::BuildObjCStringLiteral(SourceLocation AtLoc, StringLiteral *S){ + // Verify that this composite string is acceptable for ObjC strings. + if (CheckObjCString(S)) + return true; + + // Initialize the constant string interface lazily. This assumes + // the NSString interface is seen in this translation unit. Note: We + // don't use NSConstantString, since the runtime team considers this + // interface private (even though it appears in the header files). + QualType Ty = Context.getObjCConstantStringInterface(); + if (!Ty.isNull()) { + Ty = Context.getObjCObjectPointerType(Ty); + } else if (getLangOpts().NoConstantCFStrings) { + IdentifierInfo *NSIdent=nullptr; + std::string StringClass(getLangOpts().ObjCConstantStringClass); + + if (StringClass.empty()) + NSIdent = &Context.Idents.get("NSConstantString"); + else + NSIdent = &Context.Idents.get(StringClass); + + NamedDecl *IF = LookupSingleName(TUScope, NSIdent, AtLoc, + LookupOrdinaryName); + if (ObjCInterfaceDecl *StrIF = dyn_cast_or_null<ObjCInterfaceDecl>(IF)) { + Context.setObjCConstantStringInterface(StrIF); + Ty = Context.getObjCConstantStringInterface(); + Ty = Context.getObjCObjectPointerType(Ty); + } else { + // If there is no NSConstantString interface defined then treat this + // as error and recover from it. + Diag(S->getBeginLoc(), diag::err_no_nsconstant_string_class) + << NSIdent << S->getSourceRange(); + Ty = Context.getObjCIdType(); + } + } else { + IdentifierInfo *NSIdent = NSAPIObj->getNSClassId(NSAPI::ClassId_NSString); + NamedDecl *IF = LookupSingleName(TUScope, NSIdent, AtLoc, + LookupOrdinaryName); + if (ObjCInterfaceDecl *StrIF = dyn_cast_or_null<ObjCInterfaceDecl>(IF)) { + Context.setObjCConstantStringInterface(StrIF); + Ty = Context.getObjCConstantStringInterface(); + Ty = Context.getObjCObjectPointerType(Ty); + } else { + // If there is no NSString interface defined, implicitly declare + // a @class NSString; and use that instead. This is to make sure + // type of an NSString literal is represented correctly, instead of + // being an 'id' type. + Ty = Context.getObjCNSStringType(); + if (Ty.isNull()) { + ObjCInterfaceDecl *NSStringIDecl = + ObjCInterfaceDecl::Create (Context, + Context.getTranslationUnitDecl(), + SourceLocation(), NSIdent, + nullptr, nullptr, SourceLocation()); + Ty = Context.getObjCInterfaceType(NSStringIDecl); + Context.setObjCNSStringType(Ty); + } + Ty = Context.getObjCObjectPointerType(Ty); + } + } + + return new (Context) ObjCStringLiteral(S, Ty, AtLoc); +} + +/// Emits an error if the given method does not exist, or if the return +/// type is not an Objective-C object. +static bool validateBoxingMethod(Sema &S, SourceLocation Loc, + const ObjCInterfaceDecl *Class, + Selector Sel, const ObjCMethodDecl *Method) { + if (!Method) { + // FIXME: Is there a better way to avoid quotes than using getName()? + S.Diag(Loc, diag::err_undeclared_boxing_method) << Sel << Class->getName(); + return false; + } + + // Make sure the return type is reasonable. + QualType ReturnType = Method->getReturnType(); + if (!ReturnType->isObjCObjectPointerType()) { + S.Diag(Loc, diag::err_objc_literal_method_sig) + << Sel; + S.Diag(Method->getLocation(), diag::note_objc_literal_method_return) + << ReturnType; + return false; + } + + return true; +} + +/// Maps ObjCLiteralKind to NSClassIdKindKind +static NSAPI::NSClassIdKindKind ClassKindFromLiteralKind( + Sema::ObjCLiteralKind LiteralKind) { + switch (LiteralKind) { + case Sema::LK_Array: + return NSAPI::ClassId_NSArray; + case Sema::LK_Dictionary: + return NSAPI::ClassId_NSDictionary; + case Sema::LK_Numeric: + return NSAPI::ClassId_NSNumber; + case Sema::LK_String: + return NSAPI::ClassId_NSString; + case Sema::LK_Boxed: + return NSAPI::ClassId_NSValue; + + // there is no corresponding matching + // between LK_None/LK_Block and NSClassIdKindKind + case Sema::LK_Block: + case Sema::LK_None: + break; + } + llvm_unreachable("LiteralKind can't be converted into a ClassKind"); +} + +/// Validates ObjCInterfaceDecl availability. +/// ObjCInterfaceDecl, used to create ObjC literals, should be defined +/// if clang not in a debugger mode. +static bool ValidateObjCLiteralInterfaceDecl(Sema &S, ObjCInterfaceDecl *Decl, + SourceLocation Loc, + Sema::ObjCLiteralKind LiteralKind) { + if (!Decl) { + NSAPI::NSClassIdKindKind Kind = ClassKindFromLiteralKind(LiteralKind); + IdentifierInfo *II = S.NSAPIObj->getNSClassId(Kind); + S.Diag(Loc, diag::err_undeclared_objc_literal_class) + << II->getName() << LiteralKind; + return false; + } else if (!Decl->hasDefinition() && !S.getLangOpts().DebuggerObjCLiteral) { + S.Diag(Loc, diag::err_undeclared_objc_literal_class) + << Decl->getName() << LiteralKind; + S.Diag(Decl->getLocation(), diag::note_forward_class); + return false; + } + + return true; +} + +/// Looks up ObjCInterfaceDecl of a given NSClassIdKindKind. +/// Used to create ObjC literals, such as NSDictionary (@{}), +/// NSArray (@[]) and Boxed Expressions (@()) +static ObjCInterfaceDecl *LookupObjCInterfaceDeclForLiteral(Sema &S, + SourceLocation Loc, + Sema::ObjCLiteralKind LiteralKind) { + NSAPI::NSClassIdKindKind ClassKind = ClassKindFromLiteralKind(LiteralKind); + IdentifierInfo *II = S.NSAPIObj->getNSClassId(ClassKind); + NamedDecl *IF = S.LookupSingleName(S.TUScope, II, Loc, + Sema::LookupOrdinaryName); + ObjCInterfaceDecl *ID = dyn_cast_or_null<ObjCInterfaceDecl>(IF); + if (!ID && S.getLangOpts().DebuggerObjCLiteral) { + ASTContext &Context = S.Context; + TranslationUnitDecl *TU = Context.getTranslationUnitDecl(); + ID = ObjCInterfaceDecl::Create (Context, TU, SourceLocation(), II, + nullptr, nullptr, SourceLocation()); + } + + if (!ValidateObjCLiteralInterfaceDecl(S, ID, Loc, LiteralKind)) { + ID = nullptr; + } + + return ID; +} + +/// Retrieve the NSNumber factory method that should be used to create +/// an Objective-C literal for the given type. +static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc, + QualType NumberType, + bool isLiteral = false, + SourceRange R = SourceRange()) { + Optional<NSAPI::NSNumberLiteralMethodKind> Kind = + S.NSAPIObj->getNSNumberFactoryMethodKind(NumberType); + + if (!Kind) { + if (isLiteral) { + S.Diag(Loc, diag::err_invalid_nsnumber_type) + << NumberType << R; + } + return nullptr; + } + + // If we already looked up this method, we're done. + if (S.NSNumberLiteralMethods[*Kind]) + return S.NSNumberLiteralMethods[*Kind]; + + Selector Sel = S.NSAPIObj->getNSNumberLiteralSelector(*Kind, + /*Instance=*/false); + + ASTContext &CX = S.Context; + + // Look up the NSNumber class, if we haven't done so already. It's cached + // in the Sema instance. + if (!S.NSNumberDecl) { + S.NSNumberDecl = LookupObjCInterfaceDeclForLiteral(S, Loc, + Sema::LK_Numeric); + if (!S.NSNumberDecl) { + return nullptr; + } + } + + if (S.NSNumberPointer.isNull()) { + // generate the pointer to NSNumber type. + QualType NSNumberObject = CX.getObjCInterfaceType(S.NSNumberDecl); + S.NSNumberPointer = CX.getObjCObjectPointerType(NSNumberObject); + } + + // Look for the appropriate method within NSNumber. + ObjCMethodDecl *Method = S.NSNumberDecl->lookupClassMethod(Sel); + if (!Method && S.getLangOpts().DebuggerObjCLiteral) { + // create a stub definition this NSNumber factory method. + TypeSourceInfo *ReturnTInfo = nullptr; + Method = + ObjCMethodDecl::Create(CX, SourceLocation(), SourceLocation(), Sel, + S.NSNumberPointer, ReturnTInfo, S.NSNumberDecl, + /*isInstance=*/false, /*isVariadic=*/false, + /*isPropertyAccessor=*/false, + /*isSynthesizedAccessorStub=*/false, + /*isImplicitlyDeclared=*/true, + /*isDefined=*/false, ObjCMethodDecl::Required, + /*HasRelatedResultType=*/false); + ParmVarDecl *value = ParmVarDecl::Create(S.Context, Method, + SourceLocation(), SourceLocation(), + &CX.Idents.get("value"), + NumberType, /*TInfo=*/nullptr, + SC_None, nullptr); + Method->setMethodParams(S.Context, value, None); + } + + if (!validateBoxingMethod(S, Loc, S.NSNumberDecl, Sel, Method)) + return nullptr; + + // Note: if the parameter type is out-of-line, we'll catch it later in the + // implicit conversion. + + S.NSNumberLiteralMethods[*Kind] = Method; + return Method; +} + +/// BuildObjCNumericLiteral - builds an ObjCBoxedExpr AST node for the +/// numeric literal expression. Type of the expression will be "NSNumber *". +ExprResult Sema::BuildObjCNumericLiteral(SourceLocation AtLoc, Expr *Number) { + // Determine the type of the literal. + QualType NumberType = Number->getType(); + if (CharacterLiteral *Char = dyn_cast<CharacterLiteral>(Number)) { + // In C, character literals have type 'int'. That's not the type we want + // to use to determine the Objective-c literal kind. + switch (Char->getKind()) { + case CharacterLiteral::Ascii: + case CharacterLiteral::UTF8: + NumberType = Context.CharTy; + break; + + case CharacterLiteral::Wide: + NumberType = Context.getWideCharType(); + break; + + case CharacterLiteral::UTF16: + NumberType = Context.Char16Ty; + break; + + case CharacterLiteral::UTF32: + NumberType = Context.Char32Ty; + break; + } + } + + // Look for the appropriate method within NSNumber. + // Construct the literal. + SourceRange NR(Number->getSourceRange()); + ObjCMethodDecl *Method = getNSNumberFactoryMethod(*this, AtLoc, NumberType, + true, NR); + if (!Method) + return ExprError(); + + // Convert the number to the type that the parameter expects. + ParmVarDecl *ParamDecl = Method->parameters()[0]; + InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, + ParamDecl); + ExprResult ConvertedNumber = PerformCopyInitialization(Entity, + SourceLocation(), + Number); + if (ConvertedNumber.isInvalid()) + return ExprError(); + Number = ConvertedNumber.get(); + + // Use the effective source range of the literal, including the leading '@'. + return MaybeBindToTemporary( + new (Context) ObjCBoxedExpr(Number, NSNumberPointer, Method, + SourceRange(AtLoc, NR.getEnd()))); +} + +ExprResult Sema::ActOnObjCBoolLiteral(SourceLocation AtLoc, + SourceLocation ValueLoc, + bool Value) { + ExprResult Inner; + if (getLangOpts().CPlusPlus) { + Inner = ActOnCXXBoolLiteral(ValueLoc, Value? tok::kw_true : tok::kw_false); + } else { + // C doesn't actually have a way to represent literal values of type + // _Bool. So, we'll use 0/1 and implicit cast to _Bool. + Inner = ActOnIntegerConstant(ValueLoc, Value? 1 : 0); + Inner = ImpCastExprToType(Inner.get(), Context.BoolTy, + CK_IntegralToBoolean); + } + + return BuildObjCNumericLiteral(AtLoc, Inner.get()); +} + +/// Check that the given expression is a valid element of an Objective-C +/// collection literal. +static ExprResult CheckObjCCollectionLiteralElement(Sema &S, Expr *Element, + QualType T, + bool ArrayLiteral = false) { + // If the expression is type-dependent, there's nothing for us to do. + if (Element->isTypeDependent()) + return Element; + + ExprResult Result = S.CheckPlaceholderExpr(Element); + if (Result.isInvalid()) + return ExprError(); + Element = Result.get(); + + // In C++, check for an implicit conversion to an Objective-C object pointer + // type. + if (S.getLangOpts().CPlusPlus && Element->getType()->isRecordType()) { + InitializedEntity Entity + = InitializedEntity::InitializeParameter(S.Context, T, + /*Consumed=*/false); + InitializationKind Kind = InitializationKind::CreateCopy( + Element->getBeginLoc(), SourceLocation()); + InitializationSequence Seq(S, Entity, Kind, Element); + if (!Seq.Failed()) + return Seq.Perform(S, Entity, Kind, Element); + } + + Expr *OrigElement = Element; + + // Perform lvalue-to-rvalue conversion. + Result = S.DefaultLvalueConversion(Element); + if (Result.isInvalid()) + return ExprError(); + Element = Result.get(); + + // Make sure that we have an Objective-C pointer type or block. + if (!Element->getType()->isObjCObjectPointerType() && + !Element->getType()->isBlockPointerType()) { + bool Recovered = false; + + // If this is potentially an Objective-C numeric literal, add the '@'. + if (isa<IntegerLiteral>(OrigElement) || + isa<CharacterLiteral>(OrigElement) || + isa<FloatingLiteral>(OrigElement) || + isa<ObjCBoolLiteralExpr>(OrigElement) || + isa<CXXBoolLiteralExpr>(OrigElement)) { + if (S.NSAPIObj->getNSNumberFactoryMethodKind(OrigElement->getType())) { + int Which = isa<CharacterLiteral>(OrigElement) ? 1 + : (isa<CXXBoolLiteralExpr>(OrigElement) || + isa<ObjCBoolLiteralExpr>(OrigElement)) ? 2 + : 3; + + S.Diag(OrigElement->getBeginLoc(), diag::err_box_literal_collection) + << Which << OrigElement->getSourceRange() + << FixItHint::CreateInsertion(OrigElement->getBeginLoc(), "@"); + + Result = + S.BuildObjCNumericLiteral(OrigElement->getBeginLoc(), OrigElement); + if (Result.isInvalid()) + return ExprError(); + + Element = Result.get(); + Recovered = true; + } + } + // If this is potentially an Objective-C string literal, add the '@'. + else if (StringLiteral *String = dyn_cast<StringLiteral>(OrigElement)) { + if (String->isAscii()) { + S.Diag(OrigElement->getBeginLoc(), diag::err_box_literal_collection) + << 0 << OrigElement->getSourceRange() + << FixItHint::CreateInsertion(OrigElement->getBeginLoc(), "@"); + + Result = S.BuildObjCStringLiteral(OrigElement->getBeginLoc(), String); + if (Result.isInvalid()) + return ExprError(); + + Element = Result.get(); + Recovered = true; + } + } + + if (!Recovered) { + S.Diag(Element->getBeginLoc(), diag::err_invalid_collection_element) + << Element->getType(); + return ExprError(); + } + } + if (ArrayLiteral) + if (ObjCStringLiteral *getString = + dyn_cast<ObjCStringLiteral>(OrigElement)) { + if (StringLiteral *SL = getString->getString()) { + unsigned numConcat = SL->getNumConcatenated(); + if (numConcat > 1) { + // Only warn if the concatenated string doesn't come from a macro. + bool hasMacro = false; + for (unsigned i = 0; i < numConcat ; ++i) + if (SL->getStrTokenLoc(i).isMacroID()) { + hasMacro = true; + break; + } + if (!hasMacro) + S.Diag(Element->getBeginLoc(), + diag::warn_concatenated_nsarray_literal) + << Element->getType(); + } + } + } + + // Make sure that the element has the type that the container factory + // function expects. + return S.PerformCopyInitialization( + InitializedEntity::InitializeParameter(S.Context, T, + /*Consumed=*/false), + Element->getBeginLoc(), Element); +} + +ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { + if (ValueExpr->isTypeDependent()) { + ObjCBoxedExpr *BoxedExpr = + new (Context) ObjCBoxedExpr(ValueExpr, Context.DependentTy, nullptr, SR); + return BoxedExpr; + } + ObjCMethodDecl *BoxingMethod = nullptr; + QualType BoxedType; + // Convert the expression to an RValue, so we can check for pointer types... + ExprResult RValue = DefaultFunctionArrayLvalueConversion(ValueExpr); + if (RValue.isInvalid()) { + return ExprError(); + } + SourceLocation Loc = SR.getBegin(); + ValueExpr = RValue.get(); + QualType ValueType(ValueExpr->getType()); + if (const PointerType *PT = ValueType->getAs<PointerType>()) { + QualType PointeeType = PT->getPointeeType(); + if (Context.hasSameUnqualifiedType(PointeeType, Context.CharTy)) { + + if (!NSStringDecl) { + NSStringDecl = LookupObjCInterfaceDeclForLiteral(*this, Loc, + Sema::LK_String); + if (!NSStringDecl) { + return ExprError(); + } + QualType NSStringObject = Context.getObjCInterfaceType(NSStringDecl); + NSStringPointer = Context.getObjCObjectPointerType(NSStringObject); + } + + // The boxed expression can be emitted as a compile time constant if it is + // a string literal whose character encoding is compatible with UTF-8. + if (auto *CE = dyn_cast<ImplicitCastExpr>(ValueExpr)) + if (CE->getCastKind() == CK_ArrayToPointerDecay) + if (auto *SL = + dyn_cast<StringLiteral>(CE->getSubExpr()->IgnoreParens())) { + assert((SL->isAscii() || SL->isUTF8()) && + "unexpected character encoding"); + StringRef Str = SL->getString(); + const llvm::UTF8 *StrBegin = Str.bytes_begin(); + const llvm::UTF8 *StrEnd = Str.bytes_end(); + // Check that this is a valid UTF-8 string. + if (llvm::isLegalUTF8String(&StrBegin, StrEnd)) { + BoxedType = Context.getAttributedType( + AttributedType::getNullabilityAttrKind( + NullabilityKind::NonNull), + NSStringPointer, NSStringPointer); + return new (Context) ObjCBoxedExpr(CE, BoxedType, nullptr, SR); + } + + Diag(SL->getBeginLoc(), diag::warn_objc_boxing_invalid_utf8_string) + << NSStringPointer << SL->getSourceRange(); + } + + if (!StringWithUTF8StringMethod) { + IdentifierInfo *II = &Context.Idents.get("stringWithUTF8String"); + Selector stringWithUTF8String = Context.Selectors.getUnarySelector(II); + + // Look for the appropriate method within NSString. + BoxingMethod = NSStringDecl->lookupClassMethod(stringWithUTF8String); + if (!BoxingMethod && getLangOpts().DebuggerObjCLiteral) { + // Debugger needs to work even if NSString hasn't been defined. + TypeSourceInfo *ReturnTInfo = nullptr; + ObjCMethodDecl *M = ObjCMethodDecl::Create( + Context, SourceLocation(), SourceLocation(), stringWithUTF8String, + NSStringPointer, ReturnTInfo, NSStringDecl, + /*isInstance=*/false, /*isVariadic=*/false, + /*isPropertyAccessor=*/false, + /*isSynthesizedAccessorStub=*/false, + /*isImplicitlyDeclared=*/true, + /*isDefined=*/false, ObjCMethodDecl::Required, + /*HasRelatedResultType=*/false); + QualType ConstCharType = Context.CharTy.withConst(); + ParmVarDecl *value = + ParmVarDecl::Create(Context, M, + SourceLocation(), SourceLocation(), + &Context.Idents.get("value"), + Context.getPointerType(ConstCharType), + /*TInfo=*/nullptr, + SC_None, nullptr); + M->setMethodParams(Context, value, None); + BoxingMethod = M; + } + + if (!validateBoxingMethod(*this, Loc, NSStringDecl, + stringWithUTF8String, BoxingMethod)) + return ExprError(); + + StringWithUTF8StringMethod = BoxingMethod; + } + + BoxingMethod = StringWithUTF8StringMethod; + BoxedType = NSStringPointer; + // Transfer the nullability from method's return type. + Optional<NullabilityKind> Nullability = + BoxingMethod->getReturnType()->getNullability(Context); + if (Nullability) + BoxedType = Context.getAttributedType( + AttributedType::getNullabilityAttrKind(*Nullability), BoxedType, + BoxedType); + } + } else if (ValueType->isBuiltinType()) { + // The other types we support are numeric, char and BOOL/bool. We could also + // provide limited support for structure types, such as NSRange, NSRect, and + // NSSize. See NSValue (NSValueGeometryExtensions) in <Foundation/NSGeometry.h> + // for more details. + + // Check for a top-level character literal. + if (const CharacterLiteral *Char = + dyn_cast<CharacterLiteral>(ValueExpr->IgnoreParens())) { + // In C, character literals have type 'int'. That's not the type we want + // to use to determine the Objective-c literal kind. + switch (Char->getKind()) { + case CharacterLiteral::Ascii: + case CharacterLiteral::UTF8: + ValueType = Context.CharTy; + break; + + case CharacterLiteral::Wide: + ValueType = Context.getWideCharType(); + break; + + case CharacterLiteral::UTF16: + ValueType = Context.Char16Ty; + break; + + case CharacterLiteral::UTF32: + ValueType = Context.Char32Ty; + break; + } + } + // FIXME: Do I need to do anything special with BoolTy expressions? + + // Look for the appropriate method within NSNumber. + BoxingMethod = getNSNumberFactoryMethod(*this, Loc, ValueType); + BoxedType = NSNumberPointer; + } else if (const EnumType *ET = ValueType->getAs<EnumType>()) { + if (!ET->getDecl()->isComplete()) { + Diag(Loc, diag::err_objc_incomplete_boxed_expression_type) + << ValueType << ValueExpr->getSourceRange(); + return ExprError(); + } + + BoxingMethod = getNSNumberFactoryMethod(*this, Loc, + ET->getDecl()->getIntegerType()); + BoxedType = NSNumberPointer; + } else if (ValueType->isObjCBoxableRecordType()) { + // Support for structure types, that marked as objc_boxable + // struct __attribute__((objc_boxable)) s { ... }; + + // Look up the NSValue class, if we haven't done so already. It's cached + // in the Sema instance. + if (!NSValueDecl) { + NSValueDecl = LookupObjCInterfaceDeclForLiteral(*this, Loc, + Sema::LK_Boxed); + if (!NSValueDecl) { + return ExprError(); + } + + // generate the pointer to NSValue type. + QualType NSValueObject = Context.getObjCInterfaceType(NSValueDecl); + NSValuePointer = Context.getObjCObjectPointerType(NSValueObject); + } + + if (!ValueWithBytesObjCTypeMethod) { + IdentifierInfo *II[] = { + &Context.Idents.get("valueWithBytes"), + &Context.Idents.get("objCType") + }; + Selector ValueWithBytesObjCType = Context.Selectors.getSelector(2, II); + + // Look for the appropriate method within NSValue. + BoxingMethod = NSValueDecl->lookupClassMethod(ValueWithBytesObjCType); + if (!BoxingMethod && getLangOpts().DebuggerObjCLiteral) { + // Debugger needs to work even if NSValue hasn't been defined. + TypeSourceInfo *ReturnTInfo = nullptr; + ObjCMethodDecl *M = ObjCMethodDecl::Create( + Context, SourceLocation(), SourceLocation(), ValueWithBytesObjCType, + NSValuePointer, ReturnTInfo, NSValueDecl, + /*isInstance=*/false, + /*isVariadic=*/false, + /*isPropertyAccessor=*/false, + /*isSynthesizedAccessorStub=*/false, + /*isImplicitlyDeclared=*/true, + /*isDefined=*/false, ObjCMethodDecl::Required, + /*HasRelatedResultType=*/false); + + SmallVector<ParmVarDecl *, 2> Params; + + ParmVarDecl *bytes = + ParmVarDecl::Create(Context, M, + SourceLocation(), SourceLocation(), + &Context.Idents.get("bytes"), + Context.VoidPtrTy.withConst(), + /*TInfo=*/nullptr, + SC_None, nullptr); + Params.push_back(bytes); + + QualType ConstCharType = Context.CharTy.withConst(); + ParmVarDecl *type = + ParmVarDecl::Create(Context, M, + SourceLocation(), SourceLocation(), + &Context.Idents.get("type"), + Context.getPointerType(ConstCharType), + /*TInfo=*/nullptr, + SC_None, nullptr); + Params.push_back(type); + + M->setMethodParams(Context, Params, None); + BoxingMethod = M; + } + + if (!validateBoxingMethod(*this, Loc, NSValueDecl, + ValueWithBytesObjCType, BoxingMethod)) + return ExprError(); + + ValueWithBytesObjCTypeMethod = BoxingMethod; + } + + if (!ValueType.isTriviallyCopyableType(Context)) { + Diag(Loc, diag::err_objc_non_trivially_copyable_boxed_expression_type) + << ValueType << ValueExpr->getSourceRange(); + return ExprError(); + } + + BoxingMethod = ValueWithBytesObjCTypeMethod; + BoxedType = NSValuePointer; + } + + if (!BoxingMethod) { + Diag(Loc, diag::err_objc_illegal_boxed_expression_type) + << ValueType << ValueExpr->getSourceRange(); + return ExprError(); + } + + DiagnoseUseOfDecl(BoxingMethod, Loc); + + ExprResult ConvertedValueExpr; + if (ValueType->isObjCBoxableRecordType()) { + InitializedEntity IE = InitializedEntity::InitializeTemporary(ValueType); + ConvertedValueExpr = PerformCopyInitialization(IE, ValueExpr->getExprLoc(), + ValueExpr); + } else { + // Convert the expression to the type that the parameter requires. + ParmVarDecl *ParamDecl = BoxingMethod->parameters()[0]; + InitializedEntity IE = InitializedEntity::InitializeParameter(Context, + ParamDecl); + ConvertedValueExpr = PerformCopyInitialization(IE, SourceLocation(), + ValueExpr); + } + + if (ConvertedValueExpr.isInvalid()) + return ExprError(); + ValueExpr = ConvertedValueExpr.get(); + + ObjCBoxedExpr *BoxedExpr = + new (Context) ObjCBoxedExpr(ValueExpr, BoxedType, + BoxingMethod, SR); + return MaybeBindToTemporary(BoxedExpr); +} + +/// Build an ObjC subscript pseudo-object expression, given that +/// that's supported by the runtime. +ExprResult Sema::BuildObjCSubscriptExpression(SourceLocation RB, Expr *BaseExpr, + Expr *IndexExpr, + ObjCMethodDecl *getterMethod, + ObjCMethodDecl *setterMethod) { + assert(!LangOpts.isSubscriptPointerArithmetic()); + + // We can't get dependent types here; our callers should have + // filtered them out. + assert((!BaseExpr->isTypeDependent() && !IndexExpr->isTypeDependent()) && + "base or index cannot have dependent type here"); + + // Filter out placeholders in the index. In theory, overloads could + // be preserved here, although that might not actually work correctly. + ExprResult Result = CheckPlaceholderExpr(IndexExpr); + if (Result.isInvalid()) + return ExprError(); + IndexExpr = Result.get(); + + // Perform lvalue-to-rvalue conversion on the base. + Result = DefaultLvalueConversion(BaseExpr); + if (Result.isInvalid()) + return ExprError(); + BaseExpr = Result.get(); + + // Build the pseudo-object expression. + return new (Context) ObjCSubscriptRefExpr( + BaseExpr, IndexExpr, Context.PseudoObjectTy, VK_LValue, OK_ObjCSubscript, + getterMethod, setterMethod, RB); +} + +ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) { + SourceLocation Loc = SR.getBegin(); + + if (!NSArrayDecl) { + NSArrayDecl = LookupObjCInterfaceDeclForLiteral(*this, Loc, + Sema::LK_Array); + if (!NSArrayDecl) { + return ExprError(); + } + } + + // Find the arrayWithObjects:count: method, if we haven't done so already. + QualType IdT = Context.getObjCIdType(); + if (!ArrayWithObjectsMethod) { + Selector + Sel = NSAPIObj->getNSArraySelector(NSAPI::NSArr_arrayWithObjectsCount); + ObjCMethodDecl *Method = NSArrayDecl->lookupClassMethod(Sel); + if (!Method && getLangOpts().DebuggerObjCLiteral) { + TypeSourceInfo *ReturnTInfo = nullptr; + Method = ObjCMethodDecl::Create( + Context, SourceLocation(), SourceLocation(), Sel, IdT, ReturnTInfo, + Context.getTranslationUnitDecl(), false /*Instance*/, + false /*isVariadic*/, + /*isPropertyAccessor=*/false, /*isSynthesizedAccessorStub=*/false, + /*isImplicitlyDeclared=*/true, /*isDefined=*/false, + ObjCMethodDecl::Required, false); + SmallVector<ParmVarDecl *, 2> Params; + ParmVarDecl *objects = ParmVarDecl::Create(Context, Method, + SourceLocation(), + SourceLocation(), + &Context.Idents.get("objects"), + Context.getPointerType(IdT), + /*TInfo=*/nullptr, + SC_None, nullptr); + Params.push_back(objects); + ParmVarDecl *cnt = ParmVarDecl::Create(Context, Method, + SourceLocation(), + SourceLocation(), + &Context.Idents.get("cnt"), + Context.UnsignedLongTy, + /*TInfo=*/nullptr, SC_None, + nullptr); + Params.push_back(cnt); + Method->setMethodParams(Context, Params, None); + } + + if (!validateBoxingMethod(*this, Loc, NSArrayDecl, Sel, Method)) + return ExprError(); + + // Dig out the type that all elements should be converted to. + QualType T = Method->parameters()[0]->getType(); + const PointerType *PtrT = T->getAs<PointerType>(); + if (!PtrT || + !Context.hasSameUnqualifiedType(PtrT->getPointeeType(), IdT)) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << Sel; + Diag(Method->parameters()[0]->getLocation(), + diag::note_objc_literal_method_param) + << 0 << T + << Context.getPointerType(IdT.withConst()); + return ExprError(); + } + + // Check that the 'count' parameter is integral. + if (!Method->parameters()[1]->getType()->isIntegerType()) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << Sel; + Diag(Method->parameters()[1]->getLocation(), + diag::note_objc_literal_method_param) + << 1 + << Method->parameters()[1]->getType() + << "integral"; + return ExprError(); + } + + // We've found a good +arrayWithObjects:count: method. Save it! + ArrayWithObjectsMethod = Method; + } + + QualType ObjectsType = ArrayWithObjectsMethod->parameters()[0]->getType(); + QualType RequiredType = ObjectsType->castAs<PointerType>()->getPointeeType(); + + // Check that each of the elements provided is valid in a collection literal, + // performing conversions as necessary. + Expr **ElementsBuffer = Elements.data(); + for (unsigned I = 0, N = Elements.size(); I != N; ++I) { + ExprResult Converted = CheckObjCCollectionLiteralElement(*this, + ElementsBuffer[I], + RequiredType, true); + if (Converted.isInvalid()) + return ExprError(); + + ElementsBuffer[I] = Converted.get(); + } + + QualType Ty + = Context.getObjCObjectPointerType( + Context.getObjCInterfaceType(NSArrayDecl)); + + return MaybeBindToTemporary( + ObjCArrayLiteral::Create(Context, Elements, Ty, + ArrayWithObjectsMethod, SR)); +} + +/// Check for duplicate keys in an ObjC dictionary literal. For instance: +/// NSDictionary *nd = @{ @"foo" : @"bar", @"foo" : @"baz" }; +static void +CheckObjCDictionaryLiteralDuplicateKeys(Sema &S, + ObjCDictionaryLiteral *Literal) { + if (Literal->isValueDependent() || Literal->isTypeDependent()) + return; + + // NSNumber has quite relaxed equality semantics (for instance, @YES is + // considered equal to @1.0). For now, ignore floating points and just do a + // bit-width and sign agnostic integer compare. + struct APSIntCompare { + bool operator()(const llvm::APSInt &LHS, const llvm::APSInt &RHS) const { + return llvm::APSInt::compareValues(LHS, RHS) < 0; + } + }; + + llvm::DenseMap<StringRef, SourceLocation> StringKeys; + std::map<llvm::APSInt, SourceLocation, APSIntCompare> IntegralKeys; + + auto checkOneKey = [&](auto &Map, const auto &Key, SourceLocation Loc) { + auto Pair = Map.insert({Key, Loc}); + if (!Pair.second) { + S.Diag(Loc, diag::warn_nsdictionary_duplicate_key); + S.Diag(Pair.first->second, diag::note_nsdictionary_duplicate_key_here); + } + }; + + for (unsigned Idx = 0, End = Literal->getNumElements(); Idx != End; ++Idx) { + Expr *Key = Literal->getKeyValueElement(Idx).Key->IgnoreParenImpCasts(); + + if (auto *StrLit = dyn_cast<ObjCStringLiteral>(Key)) { + StringRef Bytes = StrLit->getString()->getBytes(); + SourceLocation Loc = StrLit->getExprLoc(); + checkOneKey(StringKeys, Bytes, Loc); + } + + if (auto *BE = dyn_cast<ObjCBoxedExpr>(Key)) { + Expr *Boxed = BE->getSubExpr(); + SourceLocation Loc = BE->getExprLoc(); + + // Check for @("foo"). + if (auto *Str = dyn_cast<StringLiteral>(Boxed->IgnoreParenImpCasts())) { + checkOneKey(StringKeys, Str->getBytes(), Loc); + continue; + } + + Expr::EvalResult Result; + if (Boxed->EvaluateAsInt(Result, S.getASTContext(), + Expr::SE_AllowSideEffects)) { + checkOneKey(IntegralKeys, Result.Val.getInt(), Loc); + } + } + } +} + +ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR, + MutableArrayRef<ObjCDictionaryElement> Elements) { + SourceLocation Loc = SR.getBegin(); + + if (!NSDictionaryDecl) { + NSDictionaryDecl = LookupObjCInterfaceDeclForLiteral(*this, Loc, + Sema::LK_Dictionary); + if (!NSDictionaryDecl) { + return ExprError(); + } + } + + // Find the dictionaryWithObjects:forKeys:count: method, if we haven't done + // so already. + QualType IdT = Context.getObjCIdType(); + if (!DictionaryWithObjectsMethod) { + Selector Sel = NSAPIObj->getNSDictionarySelector( + NSAPI::NSDict_dictionaryWithObjectsForKeysCount); + ObjCMethodDecl *Method = NSDictionaryDecl->lookupClassMethod(Sel); + if (!Method && getLangOpts().DebuggerObjCLiteral) { + Method = ObjCMethodDecl::Create( + Context, SourceLocation(), SourceLocation(), Sel, IdT, + nullptr /*TypeSourceInfo */, Context.getTranslationUnitDecl(), + false /*Instance*/, false /*isVariadic*/, + /*isPropertyAccessor=*/false, + /*isSynthesizedAccessorStub=*/false, + /*isImplicitlyDeclared=*/true, /*isDefined=*/false, + ObjCMethodDecl::Required, false); + SmallVector<ParmVarDecl *, 3> Params; + ParmVarDecl *objects = ParmVarDecl::Create(Context, Method, + SourceLocation(), + SourceLocation(), + &Context.Idents.get("objects"), + Context.getPointerType(IdT), + /*TInfo=*/nullptr, SC_None, + nullptr); + Params.push_back(objects); + ParmVarDecl *keys = ParmVarDecl::Create(Context, Method, + SourceLocation(), + SourceLocation(), + &Context.Idents.get("keys"), + Context.getPointerType(IdT), + /*TInfo=*/nullptr, SC_None, + nullptr); + Params.push_back(keys); + ParmVarDecl *cnt = ParmVarDecl::Create(Context, Method, + SourceLocation(), + SourceLocation(), + &Context.Idents.get("cnt"), + Context.UnsignedLongTy, + /*TInfo=*/nullptr, SC_None, + nullptr); + Params.push_back(cnt); + Method->setMethodParams(Context, Params, None); + } + + if (!validateBoxingMethod(*this, SR.getBegin(), NSDictionaryDecl, Sel, + Method)) + return ExprError(); + + // Dig out the type that all values should be converted to. + QualType ValueT = Method->parameters()[0]->getType(); + const PointerType *PtrValue = ValueT->getAs<PointerType>(); + if (!PtrValue || + !Context.hasSameUnqualifiedType(PtrValue->getPointeeType(), IdT)) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << Sel; + Diag(Method->parameters()[0]->getLocation(), + diag::note_objc_literal_method_param) + << 0 << ValueT + << Context.getPointerType(IdT.withConst()); + return ExprError(); + } + + // Dig out the type that all keys should be converted to. + QualType KeyT = Method->parameters()[1]->getType(); + const PointerType *PtrKey = KeyT->getAs<PointerType>(); + if (!PtrKey || + !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(), + IdT)) { + bool err = true; + if (PtrKey) { + if (QIDNSCopying.isNull()) { + // key argument of selector is id<NSCopying>? + if (ObjCProtocolDecl *NSCopyingPDecl = + LookupProtocol(&Context.Idents.get("NSCopying"), SR.getBegin())) { + ObjCProtocolDecl *PQ[] = {NSCopyingPDecl}; + QIDNSCopying = + Context.getObjCObjectType(Context.ObjCBuiltinIdTy, { }, + llvm::makeArrayRef( + (ObjCProtocolDecl**) PQ, + 1), + false); + QIDNSCopying = Context.getObjCObjectPointerType(QIDNSCopying); + } + } + if (!QIDNSCopying.isNull()) + err = !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(), + QIDNSCopying); + } + + if (err) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << Sel; + Diag(Method->parameters()[1]->getLocation(), + diag::note_objc_literal_method_param) + << 1 << KeyT + << Context.getPointerType(IdT.withConst()); + return ExprError(); + } + } + + // Check that the 'count' parameter is integral. + QualType CountType = Method->parameters()[2]->getType(); + if (!CountType->isIntegerType()) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << Sel; + Diag(Method->parameters()[2]->getLocation(), + diag::note_objc_literal_method_param) + << 2 << CountType + << "integral"; + return ExprError(); + } + + // We've found a good +dictionaryWithObjects:keys:count: method; save it! + DictionaryWithObjectsMethod = Method; + } + + QualType ValuesT = DictionaryWithObjectsMethod->parameters()[0]->getType(); + QualType ValueT = ValuesT->castAs<PointerType>()->getPointeeType(); + QualType KeysT = DictionaryWithObjectsMethod->parameters()[1]->getType(); + QualType KeyT = KeysT->castAs<PointerType>()->getPointeeType(); + + // Check that each of the keys and values provided is valid in a collection + // literal, performing conversions as necessary. + bool HasPackExpansions = false; + for (ObjCDictionaryElement &Element : Elements) { + // Check the key. + ExprResult Key = CheckObjCCollectionLiteralElement(*this, Element.Key, + KeyT); + if (Key.isInvalid()) + return ExprError(); + + // Check the value. + ExprResult Value + = CheckObjCCollectionLiteralElement(*this, Element.Value, ValueT); + if (Value.isInvalid()) + return ExprError(); + + Element.Key = Key.get(); + Element.Value = Value.get(); + + if (Element.EllipsisLoc.isInvalid()) + continue; + + if (!Element.Key->containsUnexpandedParameterPack() && + !Element.Value->containsUnexpandedParameterPack()) { + Diag(Element.EllipsisLoc, + diag::err_pack_expansion_without_parameter_packs) + << SourceRange(Element.Key->getBeginLoc(), + Element.Value->getEndLoc()); + return ExprError(); + } + + HasPackExpansions = true; + } + + QualType Ty = Context.getObjCObjectPointerType( + Context.getObjCInterfaceType(NSDictionaryDecl)); + + auto *Literal = + ObjCDictionaryLiteral::Create(Context, Elements, HasPackExpansions, Ty, + DictionaryWithObjectsMethod, SR); + CheckObjCDictionaryLiteralDuplicateKeys(*this, Literal); + return MaybeBindToTemporary(Literal); +} + +ExprResult Sema::BuildObjCEncodeExpression(SourceLocation AtLoc, + TypeSourceInfo *EncodedTypeInfo, + SourceLocation RParenLoc) { + QualType EncodedType = EncodedTypeInfo->getType(); + QualType StrTy; + if (EncodedType->isDependentType()) + StrTy = Context.DependentTy; + else { + if (!EncodedType->getAsArrayTypeUnsafe() && //// Incomplete array is handled. + !EncodedType->isVoidType()) // void is handled too. + if (RequireCompleteType(AtLoc, EncodedType, + diag::err_incomplete_type_objc_at_encode, + EncodedTypeInfo->getTypeLoc())) + return ExprError(); + + std::string Str; + QualType NotEncodedT; + Context.getObjCEncodingForType(EncodedType, Str, nullptr, &NotEncodedT); + if (!NotEncodedT.isNull()) + Diag(AtLoc, diag::warn_incomplete_encoded_type) + << EncodedType << NotEncodedT; + + // The type of @encode is the same as the type of the corresponding string, + // which is an array type. + StrTy = Context.getStringLiteralArrayType(Context.CharTy, Str.size()); + } + + return new (Context) ObjCEncodeExpr(StrTy, EncodedTypeInfo, AtLoc, RParenLoc); +} + +ExprResult Sema::ParseObjCEncodeExpression(SourceLocation AtLoc, + SourceLocation EncodeLoc, + SourceLocation LParenLoc, + ParsedType ty, + SourceLocation RParenLoc) { + // FIXME: Preserve type source info ? + TypeSourceInfo *TInfo; + QualType EncodedType = GetTypeFromParser(ty, &TInfo); + if (!TInfo) + TInfo = Context.getTrivialTypeSourceInfo(EncodedType, + getLocForEndOfToken(LParenLoc)); + + return BuildObjCEncodeExpression(AtLoc, TInfo, RParenLoc); +} + +static bool HelperToDiagnoseMismatchedMethodsInGlobalPool(Sema &S, + SourceLocation AtLoc, + SourceLocation LParenLoc, + SourceLocation RParenLoc, + ObjCMethodDecl *Method, + ObjCMethodList &MethList) { + ObjCMethodList *M = &MethList; + bool Warned = false; + for (M = M->getNext(); M; M=M->getNext()) { + ObjCMethodDecl *MatchingMethodDecl = M->getMethod(); + if (MatchingMethodDecl == Method || + isa<ObjCImplDecl>(MatchingMethodDecl->getDeclContext()) || + MatchingMethodDecl->getSelector() != Method->getSelector()) + continue; + if (!S.MatchTwoMethodDeclarations(Method, + MatchingMethodDecl, Sema::MMS_loose)) { + if (!Warned) { + Warned = true; + S.Diag(AtLoc, diag::warn_multiple_selectors) + << Method->getSelector() << FixItHint::CreateInsertion(LParenLoc, "(") + << FixItHint::CreateInsertion(RParenLoc, ")"); + S.Diag(Method->getLocation(), diag::note_method_declared_at) + << Method->getDeclName(); + } + S.Diag(MatchingMethodDecl->getLocation(), diag::note_method_declared_at) + << MatchingMethodDecl->getDeclName(); + } + } + return Warned; +} + +static void DiagnoseMismatchedSelectors(Sema &S, SourceLocation AtLoc, + ObjCMethodDecl *Method, + SourceLocation LParenLoc, + SourceLocation RParenLoc, + bool WarnMultipleSelectors) { + if (!WarnMultipleSelectors || + S.Diags.isIgnored(diag::warn_multiple_selectors, SourceLocation())) + return; + bool Warned = false; + for (Sema::GlobalMethodPool::iterator b = S.MethodPool.begin(), + e = S.MethodPool.end(); b != e; b++) { + // first, instance methods + ObjCMethodList &InstMethList = b->second.first; + if (HelperToDiagnoseMismatchedMethodsInGlobalPool(S, AtLoc, LParenLoc, RParenLoc, + Method, InstMethList)) + Warned = true; + + // second, class methods + ObjCMethodList &ClsMethList = b->second.second; + if (HelperToDiagnoseMismatchedMethodsInGlobalPool(S, AtLoc, LParenLoc, RParenLoc, + Method, ClsMethList) || Warned) + return; + } +} + +static ObjCMethodDecl *LookupDirectMethodInMethodList(Sema &S, Selector Sel, + ObjCMethodList &MethList, + bool &onlyDirect, + bool &anyDirect) { + (void)Sel; + ObjCMethodList *M = &MethList; + ObjCMethodDecl *DirectMethod = nullptr; + for (; M; M = M->getNext()) { + ObjCMethodDecl *Method = M->getMethod(); + if (!Method) + continue; + assert(Method->getSelector() == Sel && "Method with wrong selector in method list"); + if (Method->isDirectMethod()) { + anyDirect = true; + DirectMethod = Method; + } else + onlyDirect = false; + } + + return DirectMethod; +} + +// Search the global pool for (potentially) direct methods matching the given +// selector. If a non-direct method is found, set \param onlyDirect to false. If +// a direct method is found, set \param anyDirect to true. Returns a direct +// method, if any. +static ObjCMethodDecl *LookupDirectMethodInGlobalPool(Sema &S, Selector Sel, + bool &onlyDirect, + bool &anyDirect) { + auto Iter = S.MethodPool.find(Sel); + if (Iter == S.MethodPool.end()) + return nullptr; + + ObjCMethodDecl *DirectInstance = LookupDirectMethodInMethodList( + S, Sel, Iter->second.first, onlyDirect, anyDirect); + ObjCMethodDecl *DirectClass = LookupDirectMethodInMethodList( + S, Sel, Iter->second.second, onlyDirect, anyDirect); + + return DirectInstance ? DirectInstance : DirectClass; +} + +static ObjCMethodDecl *findMethodInCurrentClass(Sema &S, Selector Sel) { + auto *CurMD = S.getCurMethodDecl(); + if (!CurMD) + return nullptr; + ObjCInterfaceDecl *IFace = CurMD->getClassInterface(); + + // The language enforce that only one direct method is present in a given + // class, so we just need to find one method in the current class to know + // whether Sel is potentially direct in this context. + if (ObjCMethodDecl *MD = IFace->lookupMethod(Sel, /*isInstance=*/true)) + return MD; + if (ObjCMethodDecl *MD = IFace->lookupPrivateMethod(Sel, /*Instance=*/true)) + return MD; + if (ObjCMethodDecl *MD = IFace->lookupMethod(Sel, /*isInstance=*/false)) + return MD; + if (ObjCMethodDecl *MD = IFace->lookupPrivateMethod(Sel, /*Instance=*/false)) + return MD; + + return nullptr; +} + +ExprResult Sema::ParseObjCSelectorExpression(Selector Sel, + SourceLocation AtLoc, + SourceLocation SelLoc, + SourceLocation LParenLoc, + SourceLocation RParenLoc, + bool WarnMultipleSelectors) { + ObjCMethodDecl *Method = LookupInstanceMethodInGlobalPool(Sel, + SourceRange(LParenLoc, RParenLoc)); + if (!Method) + Method = LookupFactoryMethodInGlobalPool(Sel, + SourceRange(LParenLoc, RParenLoc)); + if (!Method) { + if (const ObjCMethodDecl *OM = SelectorsForTypoCorrection(Sel)) { + Selector MatchedSel = OM->getSelector(); + SourceRange SelectorRange(LParenLoc.getLocWithOffset(1), + RParenLoc.getLocWithOffset(-1)); + Diag(SelLoc, diag::warn_undeclared_selector_with_typo) + << Sel << MatchedSel + << FixItHint::CreateReplacement(SelectorRange, MatchedSel.getAsString()); + + } else + Diag(SelLoc, diag::warn_undeclared_selector) << Sel; + } else { + DiagnoseMismatchedSelectors(*this, AtLoc, Method, LParenLoc, RParenLoc, + WarnMultipleSelectors); + + bool onlyDirect = true; + bool anyDirect = false; + ObjCMethodDecl *GlobalDirectMethod = + LookupDirectMethodInGlobalPool(*this, Sel, onlyDirect, anyDirect); + + if (onlyDirect) { + Diag(AtLoc, diag::err_direct_selector_expression) + << Method->getSelector(); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } else if (anyDirect) { + // If we saw any direct methods, see if we see a direct member of the + // current class. If so, the @selector will likely be used to refer to + // this direct method. + ObjCMethodDecl *LikelyTargetMethod = findMethodInCurrentClass(*this, Sel); + if (LikelyTargetMethod && LikelyTargetMethod->isDirectMethod()) { + Diag(AtLoc, diag::warn_potentially_direct_selector_expression) << Sel; + Diag(LikelyTargetMethod->getLocation(), + diag::note_direct_method_declared_at) + << LikelyTargetMethod->getDeclName(); + } else if (!LikelyTargetMethod) { + // Otherwise, emit the "strict" variant of this diagnostic, unless + // LikelyTargetMethod is non-direct. + Diag(AtLoc, diag::warn_strict_potentially_direct_selector_expression) + << Sel; + Diag(GlobalDirectMethod->getLocation(), + diag::note_direct_method_declared_at) + << GlobalDirectMethod->getDeclName(); + } + } + } + + if (Method && + Method->getImplementationControl() != ObjCMethodDecl::Optional && + !getSourceManager().isInSystemHeader(Method->getLocation())) + ReferencedSelectors.insert(std::make_pair(Sel, AtLoc)); + + // In ARC, forbid the user from using @selector for + // retain/release/autorelease/dealloc/retainCount. + if (getLangOpts().ObjCAutoRefCount) { + switch (Sel.getMethodFamily()) { + case OMF_retain: + case OMF_release: + case OMF_autorelease: + case OMF_retainCount: + case OMF_dealloc: + Diag(AtLoc, diag::err_arc_illegal_selector) << + Sel << SourceRange(LParenLoc, RParenLoc); + break; + + case OMF_None: + case OMF_alloc: + case OMF_copy: + case OMF_finalize: + case OMF_init: + case OMF_mutableCopy: + case OMF_new: + case OMF_self: + case OMF_initialize: + case OMF_performSelector: + break; + } + } + QualType Ty = Context.getObjCSelType(); + return new (Context) ObjCSelectorExpr(Ty, Sel, AtLoc, RParenLoc); +} + +ExprResult Sema::ParseObjCProtocolExpression(IdentifierInfo *ProtocolId, + SourceLocation AtLoc, + SourceLocation ProtoLoc, + SourceLocation LParenLoc, + SourceLocation ProtoIdLoc, + SourceLocation RParenLoc) { + ObjCProtocolDecl* PDecl = LookupProtocol(ProtocolId, ProtoIdLoc); + if (!PDecl) { + Diag(ProtoLoc, diag::err_undeclared_protocol) << ProtocolId; + return true; + } + if (PDecl->isNonRuntimeProtocol()) + Diag(ProtoLoc, diag::err_objc_non_runtime_protocol_in_protocol_expr) + << PDecl; + if (!PDecl->hasDefinition()) { + Diag(ProtoLoc, diag::err_atprotocol_protocol) << PDecl; + Diag(PDecl->getLocation(), diag::note_entity_declared_at) << PDecl; + } else { + PDecl = PDecl->getDefinition(); + } + + QualType Ty = Context.getObjCProtoType(); + if (Ty.isNull()) + return true; + Ty = Context.getObjCObjectPointerType(Ty); + return new (Context) ObjCProtocolExpr(Ty, PDecl, AtLoc, ProtoIdLoc, RParenLoc); +} + +/// Try to capture an implicit reference to 'self'. +ObjCMethodDecl *Sema::tryCaptureObjCSelf(SourceLocation Loc) { + DeclContext *DC = getFunctionLevelDeclContext(); + + // If we're not in an ObjC method, error out. Note that, unlike the + // C++ case, we don't require an instance method --- class methods + // still have a 'self', and we really do still need to capture it! + ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(DC); + if (!method) + return nullptr; + + tryCaptureVariable(method->getSelfDecl(), Loc); + + return method; +} + +static QualType stripObjCInstanceType(ASTContext &Context, QualType T) { + QualType origType = T; + if (auto nullability = AttributedType::stripOuterNullability(T)) { + if (T == Context.getObjCInstanceType()) { + return Context.getAttributedType( + AttributedType::getNullabilityAttrKind(*nullability), + Context.getObjCIdType(), + Context.getObjCIdType()); + } + + return origType; + } + + if (T == Context.getObjCInstanceType()) + return Context.getObjCIdType(); + + return origType; +} + +/// Determine the result type of a message send based on the receiver type, +/// method, and the kind of message send. +/// +/// This is the "base" result type, which will still need to be adjusted +/// to account for nullability. +static QualType getBaseMessageSendResultType(Sema &S, + QualType ReceiverType, + ObjCMethodDecl *Method, + bool isClassMessage, + bool isSuperMessage) { + assert(Method && "Must have a method"); + if (!Method->hasRelatedResultType()) + return Method->getSendResultType(ReceiverType); + + ASTContext &Context = S.Context; + + // Local function that transfers the nullability of the method's + // result type to the returned result. + auto transferNullability = [&](QualType type) -> QualType { + // If the method's result type has nullability, extract it. + if (auto nullability = Method->getSendResultType(ReceiverType) + ->getNullability(Context)){ + // Strip off any outer nullability sugar from the provided type. + (void)AttributedType::stripOuterNullability(type); + + // Form a new attributed type using the method result type's nullability. + return Context.getAttributedType( + AttributedType::getNullabilityAttrKind(*nullability), + type, + type); + } + + return type; + }; + + // If a method has a related return type: + // - if the method found is an instance method, but the message send + // was a class message send, T is the declared return type of the method + // found + if (Method->isInstanceMethod() && isClassMessage) + return stripObjCInstanceType(Context, + Method->getSendResultType(ReceiverType)); + + // - if the receiver is super, T is a pointer to the class of the + // enclosing method definition + if (isSuperMessage) { + if (ObjCMethodDecl *CurMethod = S.getCurMethodDecl()) + if (ObjCInterfaceDecl *Class = CurMethod->getClassInterface()) { + return transferNullability( + Context.getObjCObjectPointerType( + Context.getObjCInterfaceType(Class))); + } + } + + // - if the receiver is the name of a class U, T is a pointer to U + if (ReceiverType->getAsObjCInterfaceType()) + return transferNullability(Context.getObjCObjectPointerType(ReceiverType)); + // - if the receiver is of type Class or qualified Class type, + // T is the declared return type of the method. + if (ReceiverType->isObjCClassType() || + ReceiverType->isObjCQualifiedClassType()) + return stripObjCInstanceType(Context, + Method->getSendResultType(ReceiverType)); + + // - if the receiver is id, qualified id, Class, or qualified Class, T + // is the receiver type, otherwise + // - T is the type of the receiver expression. + return transferNullability(ReceiverType); +} + +QualType Sema::getMessageSendResultType(const Expr *Receiver, + QualType ReceiverType, + ObjCMethodDecl *Method, + bool isClassMessage, + bool isSuperMessage) { + // Produce the result type. + QualType resultType = getBaseMessageSendResultType(*this, ReceiverType, + Method, + isClassMessage, + isSuperMessage); + + // If this is a class message, ignore the nullability of the receiver. + if (isClassMessage) { + // In a class method, class messages to 'self' that return instancetype can + // be typed as the current class. We can safely do this in ARC because self + // can't be reassigned, and we do it unsafely outside of ARC because in + // practice people never reassign self in class methods and there's some + // virtue in not being aggressively pedantic. + if (Receiver && Receiver->isObjCSelfExpr()) { + assert(ReceiverType->isObjCClassType() && "expected a Class self"); + QualType T = Method->getSendResultType(ReceiverType); + AttributedType::stripOuterNullability(T); + if (T == Context.getObjCInstanceType()) { + const ObjCMethodDecl *MD = cast<ObjCMethodDecl>( + cast<ImplicitParamDecl>( + cast<DeclRefExpr>(Receiver->IgnoreParenImpCasts())->getDecl()) + ->getDeclContext()); + assert(MD->isClassMethod() && "expected a class method"); + QualType NewResultType = Context.getObjCObjectPointerType( + Context.getObjCInterfaceType(MD->getClassInterface())); + if (auto Nullability = resultType->getNullability(Context)) + NewResultType = Context.getAttributedType( + AttributedType::getNullabilityAttrKind(*Nullability), + NewResultType, NewResultType); + return NewResultType; + } + } + return resultType; + } + + // There is nothing left to do if the result type cannot have a nullability + // specifier. + if (!resultType->canHaveNullability()) + return resultType; + + // Map the nullability of the result into a table index. + unsigned receiverNullabilityIdx = 0; + if (Optional<NullabilityKind> nullability = + ReceiverType->getNullability(Context)) { + if (*nullability == NullabilityKind::NullableResult) + nullability = NullabilityKind::Nullable; + receiverNullabilityIdx = 1 + static_cast<unsigned>(*nullability); + } + + unsigned resultNullabilityIdx = 0; + if (Optional<NullabilityKind> nullability = + resultType->getNullability(Context)) { + if (*nullability == NullabilityKind::NullableResult) + nullability = NullabilityKind::Nullable; + resultNullabilityIdx = 1 + static_cast<unsigned>(*nullability); + } + + // The table of nullability mappings, indexed by the receiver's nullability + // and then the result type's nullability. + static const uint8_t None = 0; + static const uint8_t NonNull = 1; + static const uint8_t Nullable = 2; + static const uint8_t Unspecified = 3; + static const uint8_t nullabilityMap[4][4] = { + // None NonNull Nullable Unspecified + /* None */ { None, None, Nullable, None }, + /* NonNull */ { None, NonNull, Nullable, Unspecified }, + /* Nullable */ { Nullable, Nullable, Nullable, Nullable }, + /* Unspecified */ { None, Unspecified, Nullable, Unspecified } + }; + + unsigned newResultNullabilityIdx + = nullabilityMap[receiverNullabilityIdx][resultNullabilityIdx]; + if (newResultNullabilityIdx == resultNullabilityIdx) + return resultType; + + // Strip off the existing nullability. This removes as little type sugar as + // possible. + do { + if (auto attributed = dyn_cast<AttributedType>(resultType.getTypePtr())) { + resultType = attributed->getModifiedType(); + } else { + resultType = resultType.getDesugaredType(Context); + } + } while (resultType->getNullability(Context)); + + // Add nullability back if needed. + if (newResultNullabilityIdx > 0) { + auto newNullability + = static_cast<NullabilityKind>(newResultNullabilityIdx-1); + return Context.getAttributedType( + AttributedType::getNullabilityAttrKind(newNullability), + resultType, resultType); + } + + return resultType; +} + +/// Look for an ObjC method whose result type exactly matches the given type. +static const ObjCMethodDecl * +findExplicitInstancetypeDeclarer(const ObjCMethodDecl *MD, + QualType instancetype) { + if (MD->getReturnType() == instancetype) + return MD; + + // For these purposes, a method in an @implementation overrides a + // declaration in the @interface. + if (const ObjCImplDecl *impl = + dyn_cast<ObjCImplDecl>(MD->getDeclContext())) { + const ObjCContainerDecl *iface; + if (const ObjCCategoryImplDecl *catImpl = + dyn_cast<ObjCCategoryImplDecl>(impl)) { + iface = catImpl->getCategoryDecl(); + } else { + iface = impl->getClassInterface(); + } + + const ObjCMethodDecl *ifaceMD = + iface->getMethod(MD->getSelector(), MD->isInstanceMethod()); + if (ifaceMD) return findExplicitInstancetypeDeclarer(ifaceMD, instancetype); + } + + SmallVector<const ObjCMethodDecl *, 4> overrides; + MD->getOverriddenMethods(overrides); + for (unsigned i = 0, e = overrides.size(); i != e; ++i) { + if (const ObjCMethodDecl *result = + findExplicitInstancetypeDeclarer(overrides[i], instancetype)) + return result; + } + + return nullptr; +} + +void Sema::EmitRelatedResultTypeNoteForReturn(QualType destType) { + // Only complain if we're in an ObjC method and the required return + // type doesn't match the method's declared return type. + ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CurContext); + if (!MD || !MD->hasRelatedResultType() || + Context.hasSameUnqualifiedType(destType, MD->getReturnType())) + return; + + // Look for a method overridden by this method which explicitly uses + // 'instancetype'. + if (const ObjCMethodDecl *overridden = + findExplicitInstancetypeDeclarer(MD, Context.getObjCInstanceType())) { + SourceRange range = overridden->getReturnTypeSourceRange(); + SourceLocation loc = range.getBegin(); + if (loc.isInvalid()) + loc = overridden->getLocation(); + Diag(loc, diag::note_related_result_type_explicit) + << /*current method*/ 1 << range; + return; + } + + // Otherwise, if we have an interesting method family, note that. + // This should always trigger if the above didn't. + if (ObjCMethodFamily family = MD->getMethodFamily()) + Diag(MD->getLocation(), diag::note_related_result_type_family) + << /*current method*/ 1 + << family; +} + +void Sema::EmitRelatedResultTypeNote(const Expr *E) { + E = E->IgnoreParenImpCasts(); + const ObjCMessageExpr *MsgSend = dyn_cast<ObjCMessageExpr>(E); + if (!MsgSend) + return; + + const ObjCMethodDecl *Method = MsgSend->getMethodDecl(); + if (!Method) + return; + + if (!Method->hasRelatedResultType()) + return; + + if (Context.hasSameUnqualifiedType( + Method->getReturnType().getNonReferenceType(), MsgSend->getType())) + return; + + if (!Context.hasSameUnqualifiedType(Method->getReturnType(), + Context.getObjCInstanceType())) + return; + + Diag(Method->getLocation(), diag::note_related_result_type_inferred) + << Method->isInstanceMethod() << Method->getSelector() + << MsgSend->getType(); +} + +bool Sema::CheckMessageArgumentTypes( + const Expr *Receiver, QualType ReceiverType, MultiExprArg Args, + Selector Sel, ArrayRef<SourceLocation> SelectorLocs, ObjCMethodDecl *Method, + bool isClassMessage, bool isSuperMessage, SourceLocation lbrac, + SourceLocation rbrac, SourceRange RecRange, QualType &ReturnType, + ExprValueKind &VK) { + SourceLocation SelLoc; + if (!SelectorLocs.empty() && SelectorLocs.front().isValid()) + SelLoc = SelectorLocs.front(); + else + SelLoc = lbrac; + + if (!Method) { + // Apply default argument promotion as for (C99 6.5.2.2p6). + for (unsigned i = 0, e = Args.size(); i != e; i++) { + if (Args[i]->isTypeDependent()) + continue; + + ExprResult result; + if (getLangOpts().DebuggerSupport) { + QualType paramTy; // ignored + result = checkUnknownAnyArg(SelLoc, Args[i], paramTy); + } else { + result = DefaultArgumentPromotion(Args[i]); + } + if (result.isInvalid()) + return true; + Args[i] = result.get(); + } + + unsigned DiagID; + if (getLangOpts().ObjCAutoRefCount) + DiagID = diag::err_arc_method_not_found; + else + DiagID = isClassMessage ? diag::warn_class_method_not_found + : diag::warn_inst_method_not_found; + if (!getLangOpts().DebuggerSupport) { + const ObjCMethodDecl *OMD = SelectorsForTypoCorrection(Sel, ReceiverType); + if (OMD && !OMD->isInvalidDecl()) { + if (getLangOpts().ObjCAutoRefCount) + DiagID = diag::err_method_not_found_with_typo; + else + DiagID = isClassMessage ? diag::warn_class_method_not_found_with_typo + : diag::warn_instance_method_not_found_with_typo; + Selector MatchedSel = OMD->getSelector(); + SourceRange SelectorRange(SelectorLocs.front(), SelectorLocs.back()); + if (MatchedSel.isUnarySelector()) + Diag(SelLoc, DiagID) + << Sel<< isClassMessage << MatchedSel + << FixItHint::CreateReplacement(SelectorRange, MatchedSel.getAsString()); + else + Diag(SelLoc, DiagID) << Sel<< isClassMessage << MatchedSel; + } + else + Diag(SelLoc, DiagID) + << Sel << isClassMessage << SourceRange(SelectorLocs.front(), + SelectorLocs.back()); + // Find the class to which we are sending this message. + if (auto *ObjPT = ReceiverType->getAs<ObjCObjectPointerType>()) { + if (ObjCInterfaceDecl *ThisClass = ObjPT->getInterfaceDecl()) { + Diag(ThisClass->getLocation(), diag::note_receiver_class_declared); + if (!RecRange.isInvalid()) + if (ThisClass->lookupClassMethod(Sel)) + Diag(RecRange.getBegin(), diag::note_receiver_expr_here) + << FixItHint::CreateReplacement(RecRange, + ThisClass->getNameAsString()); + } + } + } + + // In debuggers, we want to use __unknown_anytype for these + // results so that clients can cast them. + if (getLangOpts().DebuggerSupport) { + ReturnType = Context.UnknownAnyTy; + } else { + ReturnType = Context.getObjCIdType(); + } + VK = VK_PRValue; + return false; + } + + ReturnType = getMessageSendResultType(Receiver, ReceiverType, Method, + isClassMessage, isSuperMessage); + VK = Expr::getValueKindForType(Method->getReturnType()); + + unsigned NumNamedArgs = Sel.getNumArgs(); + // Method might have more arguments than selector indicates. This is due + // to addition of c-style arguments in method. + if (Method->param_size() > Sel.getNumArgs()) + NumNamedArgs = Method->param_size(); + // FIXME. This need be cleaned up. + if (Args.size() < NumNamedArgs) { + Diag(SelLoc, diag::err_typecheck_call_too_few_args) + << 2 << NumNamedArgs << static_cast<unsigned>(Args.size()); + return false; + } + + // Compute the set of type arguments to be substituted into each parameter + // type. + Optional<ArrayRef<QualType>> typeArgs + = ReceiverType->getObjCSubstitutions(Method->getDeclContext()); + bool IsError = false; + for (unsigned i = 0; i < NumNamedArgs; i++) { + // We can't do any type-checking on a type-dependent argument. + if (Args[i]->isTypeDependent()) + continue; + + Expr *argExpr = Args[i]; + + ParmVarDecl *param = Method->parameters()[i]; + assert(argExpr && "CheckMessageArgumentTypes(): missing expression"); + + if (param->hasAttr<NoEscapeAttr>() && + param->getType()->isBlockPointerType()) + if (auto *BE = dyn_cast<BlockExpr>( + argExpr->IgnoreParenNoopCasts(Context))) + BE->getBlockDecl()->setDoesNotEscape(); + + // Strip the unbridged-cast placeholder expression off unless it's + // a consumed argument. + if (argExpr->hasPlaceholderType(BuiltinType::ARCUnbridgedCast) && + !param->hasAttr<CFConsumedAttr>()) + argExpr = stripARCUnbridgedCast(argExpr); + + // If the parameter is __unknown_anytype, infer its type + // from the argument. + if (param->getType() == Context.UnknownAnyTy) { + QualType paramType; + ExprResult argE = checkUnknownAnyArg(SelLoc, argExpr, paramType); + if (argE.isInvalid()) { + IsError = true; + } else { + Args[i] = argE.get(); + + // Update the parameter type in-place. + param->setType(paramType); + } + continue; + } + + QualType origParamType = param->getType(); + QualType paramType = param->getType(); + if (typeArgs) + paramType = paramType.substObjCTypeArgs( + Context, + *typeArgs, + ObjCSubstitutionContext::Parameter); + + if (RequireCompleteType(argExpr->getSourceRange().getBegin(), + paramType, + diag::err_call_incomplete_argument, argExpr)) + return true; + + InitializedEntity Entity + = InitializedEntity::InitializeParameter(Context, param, paramType); + ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(), argExpr); + if (ArgE.isInvalid()) + IsError = true; + else { + Args[i] = ArgE.getAs<Expr>(); + + // If we are type-erasing a block to a block-compatible + // Objective-C pointer type, we may need to extend the lifetime + // of the block object. + if (typeArgs && Args[i]->isPRValue() && paramType->isBlockPointerType() && + Args[i]->getType()->isBlockPointerType() && + origParamType->isObjCObjectPointerType()) { + ExprResult arg = Args[i]; + maybeExtendBlockObject(arg); + Args[i] = arg.get(); + } + } + } + + // Promote additional arguments to variadic methods. + if (Method->isVariadic()) { + for (unsigned i = NumNamedArgs, e = Args.size(); i < e; ++i) { + if (Args[i]->isTypeDependent()) + continue; + + ExprResult Arg = DefaultVariadicArgumentPromotion(Args[i], VariadicMethod, + nullptr); + IsError |= Arg.isInvalid(); + Args[i] = Arg.get(); + } + } else { + // Check for extra arguments to non-variadic methods. + if (Args.size() != NumNamedArgs) { + Diag(Args[NumNamedArgs]->getBeginLoc(), + diag::err_typecheck_call_too_many_args) + << 2 /*method*/ << NumNamedArgs << static_cast<unsigned>(Args.size()) + << Method->getSourceRange() + << SourceRange(Args[NumNamedArgs]->getBeginLoc(), + Args.back()->getEndLoc()); + } + } + + DiagnoseSentinelCalls(Method, SelLoc, Args); + + // Do additional checkings on method. + IsError |= CheckObjCMethodCall( + Method, SelLoc, makeArrayRef(Args.data(), Args.size())); + + return IsError; +} + +bool Sema::isSelfExpr(Expr *RExpr) { + // 'self' is objc 'self' in an objc method only. + ObjCMethodDecl *Method = + dyn_cast_or_null<ObjCMethodDecl>(CurContext->getNonClosureAncestor()); + return isSelfExpr(RExpr, Method); +} + +bool Sema::isSelfExpr(Expr *receiver, const ObjCMethodDecl *method) { + if (!method) return false; + + receiver = receiver->IgnoreParenLValueCasts(); + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(receiver)) + if (DRE->getDecl() == method->getSelfDecl()) + return true; + return false; +} + +/// LookupMethodInType - Look up a method in an ObjCObjectType. +ObjCMethodDecl *Sema::LookupMethodInObjectType(Selector sel, QualType type, + bool isInstance) { + const ObjCObjectType *objType = type->castAs<ObjCObjectType>(); + if (ObjCInterfaceDecl *iface = objType->getInterface()) { + // Look it up in the main interface (and categories, etc.) + if (ObjCMethodDecl *method = iface->lookupMethod(sel, isInstance)) + return method; + + // Okay, look for "private" methods declared in any + // @implementations we've seen. + if (ObjCMethodDecl *method = iface->lookupPrivateMethod(sel, isInstance)) + return method; + } + + // Check qualifiers. + for (const auto *I : objType->quals()) + if (ObjCMethodDecl *method = I->lookupMethod(sel, isInstance)) + return method; + + return nullptr; +} + +/// LookupMethodInQualifiedType - Lookups up a method in protocol qualifier +/// list of a qualified objective pointer type. +ObjCMethodDecl *Sema::LookupMethodInQualifiedType(Selector Sel, + const ObjCObjectPointerType *OPT, + bool Instance) +{ + ObjCMethodDecl *MD = nullptr; + for (const auto *PROTO : OPT->quals()) { + if ((MD = PROTO->lookupMethod(Sel, Instance))) { + return MD; + } + } + return nullptr; +} + +/// HandleExprPropertyRefExpr - Handle foo.bar where foo is a pointer to an +/// objective C interface. This is a property reference expression. +ExprResult Sema:: +HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, + Expr *BaseExpr, SourceLocation OpLoc, + DeclarationName MemberName, + SourceLocation MemberLoc, + SourceLocation SuperLoc, QualType SuperType, + bool Super) { + const ObjCInterfaceType *IFaceT = OPT->getInterfaceType(); + ObjCInterfaceDecl *IFace = IFaceT->getDecl(); + + if (!MemberName.isIdentifier()) { + Diag(MemberLoc, diag::err_invalid_property_name) + << MemberName << QualType(OPT, 0); + return ExprError(); + } + + IdentifierInfo *Member = MemberName.getAsIdentifierInfo(); + + SourceRange BaseRange = Super? SourceRange(SuperLoc) + : BaseExpr->getSourceRange(); + if (RequireCompleteType(MemberLoc, OPT->getPointeeType(), + diag::err_property_not_found_forward_class, + MemberName, BaseRange)) + return ExprError(); + + if (ObjCPropertyDecl *PD = IFace->FindPropertyDeclaration( + Member, ObjCPropertyQueryKind::OBJC_PR_query_instance)) { + // Check whether we can reference this property. + if (DiagnoseUseOfDecl(PD, MemberLoc)) + return ExprError(); + if (Super) + return new (Context) + ObjCPropertyRefExpr(PD, Context.PseudoObjectTy, VK_LValue, + OK_ObjCProperty, MemberLoc, SuperLoc, SuperType); + else + return new (Context) + ObjCPropertyRefExpr(PD, Context.PseudoObjectTy, VK_LValue, + OK_ObjCProperty, MemberLoc, BaseExpr); + } + // Check protocols on qualified interfaces. + for (const auto *I : OPT->quals()) + if (ObjCPropertyDecl *PD = I->FindPropertyDeclaration( + Member, ObjCPropertyQueryKind::OBJC_PR_query_instance)) { + // Check whether we can reference this property. + if (DiagnoseUseOfDecl(PD, MemberLoc)) + return ExprError(); + + if (Super) + return new (Context) ObjCPropertyRefExpr( + PD, Context.PseudoObjectTy, VK_LValue, OK_ObjCProperty, MemberLoc, + SuperLoc, SuperType); + else + return new (Context) + ObjCPropertyRefExpr(PD, Context.PseudoObjectTy, VK_LValue, + OK_ObjCProperty, MemberLoc, BaseExpr); + } + // If that failed, look for an "implicit" property by seeing if the nullary + // selector is implemented. + + // FIXME: The logic for looking up nullary and unary selectors should be + // shared with the code in ActOnInstanceMessage. + + Selector Sel = PP.getSelectorTable().getNullarySelector(Member); + ObjCMethodDecl *Getter = IFace->lookupInstanceMethod(Sel); + + // May be found in property's qualified list. + if (!Getter) + Getter = LookupMethodInQualifiedType(Sel, OPT, true); + + // If this reference is in an @implementation, check for 'private' methods. + if (!Getter) + Getter = IFace->lookupPrivateMethod(Sel); + + if (Getter) { + // Check if we can reference this property. + if (DiagnoseUseOfDecl(Getter, MemberLoc)) + return ExprError(); + } + // If we found a getter then this may be a valid dot-reference, we + // will look for the matching setter, in case it is needed. + Selector SetterSel = + SelectorTable::constructSetterSelector(PP.getIdentifierTable(), + PP.getSelectorTable(), Member); + ObjCMethodDecl *Setter = IFace->lookupInstanceMethod(SetterSel); + + // May be found in property's qualified list. + if (!Setter) + Setter = LookupMethodInQualifiedType(SetterSel, OPT, true); + + if (!Setter) { + // If this reference is in an @implementation, also check for 'private' + // methods. + Setter = IFace->lookupPrivateMethod(SetterSel); + } + + if (Setter && DiagnoseUseOfDecl(Setter, MemberLoc)) + return ExprError(); + + // Special warning if member name used in a property-dot for a setter accessor + // does not use a property with same name; e.g. obj.X = ... for a property with + // name 'x'. + if (Setter && Setter->isImplicit() && Setter->isPropertyAccessor() && + !IFace->FindPropertyDeclaration( + Member, ObjCPropertyQueryKind::OBJC_PR_query_instance)) { + if (const ObjCPropertyDecl *PDecl = Setter->findPropertyDecl()) { + // Do not warn if user is using property-dot syntax to make call to + // user named setter. + if (!(PDecl->getPropertyAttributes() & + ObjCPropertyAttribute::kind_setter)) + Diag(MemberLoc, + diag::warn_property_access_suggest) + << MemberName << QualType(OPT, 0) << PDecl->getName() + << FixItHint::CreateReplacement(MemberLoc, PDecl->getName()); + } + } + + if (Getter || Setter) { + if (Super) + return new (Context) + ObjCPropertyRefExpr(Getter, Setter, Context.PseudoObjectTy, VK_LValue, + OK_ObjCProperty, MemberLoc, SuperLoc, SuperType); + else + return new (Context) + ObjCPropertyRefExpr(Getter, Setter, Context.PseudoObjectTy, VK_LValue, + OK_ObjCProperty, MemberLoc, BaseExpr); + + } + + // Attempt to correct for typos in property names. + DeclFilterCCC<ObjCPropertyDecl> CCC{}; + if (TypoCorrection Corrected = CorrectTypo( + DeclarationNameInfo(MemberName, MemberLoc), LookupOrdinaryName, + nullptr, nullptr, CCC, CTK_ErrorRecovery, IFace, false, OPT)) { + DeclarationName TypoResult = Corrected.getCorrection(); + if (TypoResult.isIdentifier() && + TypoResult.getAsIdentifierInfo() == Member) { + // There is no need to try the correction if it is the same. + NamedDecl *ChosenDecl = + Corrected.isKeyword() ? nullptr : Corrected.getFoundDecl(); + if (ChosenDecl && isa<ObjCPropertyDecl>(ChosenDecl)) + if (cast<ObjCPropertyDecl>(ChosenDecl)->isClassProperty()) { + // This is a class property, we should not use the instance to + // access it. + Diag(MemberLoc, diag::err_class_property_found) << MemberName + << OPT->getInterfaceDecl()->getName() + << FixItHint::CreateReplacement(BaseExpr->getSourceRange(), + OPT->getInterfaceDecl()->getName()); + return ExprError(); + } + } else { + diagnoseTypo(Corrected, PDiag(diag::err_property_not_found_suggest) + << MemberName << QualType(OPT, 0)); + return HandleExprPropertyRefExpr(OPT, BaseExpr, OpLoc, + TypoResult, MemberLoc, + SuperLoc, SuperType, Super); + } + } + ObjCInterfaceDecl *ClassDeclared; + if (ObjCIvarDecl *Ivar = + IFace->lookupInstanceVariable(Member, ClassDeclared)) { + QualType T = Ivar->getType(); + if (const ObjCObjectPointerType * OBJPT = + T->getAsObjCInterfacePointerType()) { + if (RequireCompleteType(MemberLoc, OBJPT->getPointeeType(), + diag::err_property_not_as_forward_class, + MemberName, BaseExpr)) + return ExprError(); + } + Diag(MemberLoc, + diag::err_ivar_access_using_property_syntax_suggest) + << MemberName << QualType(OPT, 0) << Ivar->getDeclName() + << FixItHint::CreateReplacement(OpLoc, "->"); + return ExprError(); + } + + Diag(MemberLoc, diag::err_property_not_found) + << MemberName << QualType(OPT, 0); + if (Setter) + Diag(Setter->getLocation(), diag::note_getter_unavailable) + << MemberName << BaseExpr->getSourceRange(); + return ExprError(); +} + +ExprResult Sema:: +ActOnClassPropertyRefExpr(IdentifierInfo &receiverName, + IdentifierInfo &propertyName, + SourceLocation receiverNameLoc, + SourceLocation propertyNameLoc) { + + IdentifierInfo *receiverNamePtr = &receiverName; + ObjCInterfaceDecl *IFace = getObjCInterfaceDecl(receiverNamePtr, + receiverNameLoc); + + QualType SuperType; + if (!IFace) { + // If the "receiver" is 'super' in a method, handle it as an expression-like + // property reference. + if (receiverNamePtr->isStr("super")) { + if (ObjCMethodDecl *CurMethod = tryCaptureObjCSelf(receiverNameLoc)) { + if (auto classDecl = CurMethod->getClassInterface()) { + SuperType = QualType(classDecl->getSuperClassType(), 0); + if (CurMethod->isInstanceMethod()) { + if (SuperType.isNull()) { + // The current class does not have a superclass. + Diag(receiverNameLoc, diag::err_root_class_cannot_use_super) + << CurMethod->getClassInterface()->getIdentifier(); + return ExprError(); + } + QualType T = Context.getObjCObjectPointerType(SuperType); + + return HandleExprPropertyRefExpr(T->castAs<ObjCObjectPointerType>(), + /*BaseExpr*/nullptr, + SourceLocation()/*OpLoc*/, + &propertyName, + propertyNameLoc, + receiverNameLoc, T, true); + } + + // Otherwise, if this is a class method, try dispatching to our + // superclass. + IFace = CurMethod->getClassInterface()->getSuperClass(); + } + } + } + + if (!IFace) { + Diag(receiverNameLoc, diag::err_expected_either) << tok::identifier + << tok::l_paren; + return ExprError(); + } + } + + Selector GetterSel; + Selector SetterSel; + if (auto PD = IFace->FindPropertyDeclaration( + &propertyName, ObjCPropertyQueryKind::OBJC_PR_query_class)) { + GetterSel = PD->getGetterName(); + SetterSel = PD->getSetterName(); + } else { + GetterSel = PP.getSelectorTable().getNullarySelector(&propertyName); + SetterSel = SelectorTable::constructSetterSelector( + PP.getIdentifierTable(), PP.getSelectorTable(), &propertyName); + } + + // Search for a declared property first. + ObjCMethodDecl *Getter = IFace->lookupClassMethod(GetterSel); + + // If this reference is in an @implementation, check for 'private' methods. + if (!Getter) + Getter = IFace->lookupPrivateClassMethod(GetterSel); + + if (Getter) { + // FIXME: refactor/share with ActOnMemberReference(). + // Check if we can reference this property. + if (DiagnoseUseOfDecl(Getter, propertyNameLoc)) + return ExprError(); + } + + // Look for the matching setter, in case it is needed. + ObjCMethodDecl *Setter = IFace->lookupClassMethod(SetterSel); + if (!Setter) { + // If this reference is in an @implementation, also check for 'private' + // methods. + Setter = IFace->lookupPrivateClassMethod(SetterSel); + } + // Look through local category implementations associated with the class. + if (!Setter) + Setter = IFace->getCategoryClassMethod(SetterSel); + + if (Setter && DiagnoseUseOfDecl(Setter, propertyNameLoc)) + return ExprError(); + + if (Getter || Setter) { + if (!SuperType.isNull()) + return new (Context) + ObjCPropertyRefExpr(Getter, Setter, Context.PseudoObjectTy, VK_LValue, + OK_ObjCProperty, propertyNameLoc, receiverNameLoc, + SuperType); + + return new (Context) ObjCPropertyRefExpr( + Getter, Setter, Context.PseudoObjectTy, VK_LValue, OK_ObjCProperty, + propertyNameLoc, receiverNameLoc, IFace); + } + return ExprError(Diag(propertyNameLoc, diag::err_property_not_found) + << &propertyName << Context.getObjCInterfaceType(IFace)); +} + +namespace { + +class ObjCInterfaceOrSuperCCC final : public CorrectionCandidateCallback { + public: + ObjCInterfaceOrSuperCCC(ObjCMethodDecl *Method) { + // Determine whether "super" is acceptable in the current context. + if (Method && Method->getClassInterface()) + WantObjCSuper = Method->getClassInterface()->getSuperClass(); + } + + bool ValidateCandidate(const TypoCorrection &candidate) override { + return candidate.getCorrectionDeclAs<ObjCInterfaceDecl>() || + candidate.isKeyword("super"); + } + + std::unique_ptr<CorrectionCandidateCallback> clone() override { + return std::make_unique<ObjCInterfaceOrSuperCCC>(*this); + } +}; + +} // end anonymous namespace + +Sema::ObjCMessageKind Sema::getObjCMessageKind(Scope *S, + IdentifierInfo *Name, + SourceLocation NameLoc, + bool IsSuper, + bool HasTrailingDot, + ParsedType &ReceiverType) { + ReceiverType = nullptr; + + // If the identifier is "super" and there is no trailing dot, we're + // messaging super. If the identifier is "super" and there is a + // trailing dot, it's an instance message. + if (IsSuper && S->isInObjcMethodScope()) + return HasTrailingDot? ObjCInstanceMessage : ObjCSuperMessage; + + LookupResult Result(*this, Name, NameLoc, LookupOrdinaryName); + LookupName(Result, S); + + switch (Result.getResultKind()) { + case LookupResult::NotFound: + // Normal name lookup didn't find anything. If we're in an + // Objective-C method, look for ivars. If we find one, we're done! + // FIXME: This is a hack. Ivar lookup should be part of normal + // lookup. + if (ObjCMethodDecl *Method = getCurMethodDecl()) { + if (!Method->getClassInterface()) { + // Fall back: let the parser try to parse it as an instance message. + return ObjCInstanceMessage; + } + + ObjCInterfaceDecl *ClassDeclared; + if (Method->getClassInterface()->lookupInstanceVariable(Name, + ClassDeclared)) + return ObjCInstanceMessage; + } + + // Break out; we'll perform typo correction below. + break; + + case LookupResult::NotFoundInCurrentInstantiation: + case LookupResult::FoundOverloaded: + case LookupResult::FoundUnresolvedValue: + case LookupResult::Ambiguous: + Result.suppressDiagnostics(); + return ObjCInstanceMessage; + + case LookupResult::Found: { + // If the identifier is a class or not, and there is a trailing dot, + // it's an instance message. + if (HasTrailingDot) + return ObjCInstanceMessage; + // We found something. If it's a type, then we have a class + // message. Otherwise, it's an instance message. + NamedDecl *ND = Result.getFoundDecl(); + QualType T; + if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(ND)) + T = Context.getObjCInterfaceType(Class); + else if (TypeDecl *Type = dyn_cast<TypeDecl>(ND)) { + T = Context.getTypeDeclType(Type); + DiagnoseUseOfDecl(Type, NameLoc); + } + else + return ObjCInstanceMessage; + + // We have a class message, and T is the type we're + // messaging. Build source-location information for it. + TypeSourceInfo *TSInfo = Context.getTrivialTypeSourceInfo(T, NameLoc); + ReceiverType = CreateParsedType(T, TSInfo); + return ObjCClassMessage; + } + } + + ObjCInterfaceOrSuperCCC CCC(getCurMethodDecl()); + if (TypoCorrection Corrected = CorrectTypo( + Result.getLookupNameInfo(), Result.getLookupKind(), S, nullptr, CCC, + CTK_ErrorRecovery, nullptr, false, nullptr, false)) { + if (Corrected.isKeyword()) { + // If we've found the keyword "super" (the only keyword that would be + // returned by CorrectTypo), this is a send to super. + diagnoseTypo(Corrected, + PDiag(diag::err_unknown_receiver_suggest) << Name); + return ObjCSuperMessage; + } else if (ObjCInterfaceDecl *Class = + Corrected.getCorrectionDeclAs<ObjCInterfaceDecl>()) { + // If we found a declaration, correct when it refers to an Objective-C + // class. + diagnoseTypo(Corrected, + PDiag(diag::err_unknown_receiver_suggest) << Name); + QualType T = Context.getObjCInterfaceType(Class); + TypeSourceInfo *TSInfo = Context.getTrivialTypeSourceInfo(T, NameLoc); + ReceiverType = CreateParsedType(T, TSInfo); + return ObjCClassMessage; + } + } + + // Fall back: let the parser try to parse it as an instance message. + return ObjCInstanceMessage; +} + +ExprResult Sema::ActOnSuperMessage(Scope *S, + SourceLocation SuperLoc, + Selector Sel, + SourceLocation LBracLoc, + ArrayRef<SourceLocation> SelectorLocs, + SourceLocation RBracLoc, + MultiExprArg Args) { + // Determine whether we are inside a method or not. + ObjCMethodDecl *Method = tryCaptureObjCSelf(SuperLoc); + if (!Method) { + Diag(SuperLoc, diag::err_invalid_receiver_to_message_super); + return ExprError(); + } + + ObjCInterfaceDecl *Class = Method->getClassInterface(); + if (!Class) { + Diag(SuperLoc, diag::err_no_super_class_message) + << Method->getDeclName(); + return ExprError(); + } + + QualType SuperTy(Class->getSuperClassType(), 0); + if (SuperTy.isNull()) { + // The current class does not have a superclass. + Diag(SuperLoc, diag::err_root_class_cannot_use_super) + << Class->getIdentifier(); + return ExprError(); + } + + // We are in a method whose class has a superclass, so 'super' + // is acting as a keyword. + if (Method->getSelector() == Sel) + getCurFunction()->ObjCShouldCallSuper = false; + + if (Method->isInstanceMethod()) { + // Since we are in an instance method, this is an instance + // message to the superclass instance. + SuperTy = Context.getObjCObjectPointerType(SuperTy); + return BuildInstanceMessage(nullptr, SuperTy, SuperLoc, + Sel, /*Method=*/nullptr, + LBracLoc, SelectorLocs, RBracLoc, Args); + } + + // Since we are in a class method, this is a class message to + // the superclass. + return BuildClassMessage(/*ReceiverTypeInfo=*/nullptr, + SuperTy, + SuperLoc, Sel, /*Method=*/nullptr, + LBracLoc, SelectorLocs, RBracLoc, Args); +} + +ExprResult Sema::BuildClassMessageImplicit(QualType ReceiverType, + bool isSuperReceiver, + SourceLocation Loc, + Selector Sel, + ObjCMethodDecl *Method, + MultiExprArg Args) { + TypeSourceInfo *receiverTypeInfo = nullptr; + if (!ReceiverType.isNull()) + receiverTypeInfo = Context.getTrivialTypeSourceInfo(ReceiverType); + + return BuildClassMessage(receiverTypeInfo, ReceiverType, + /*SuperLoc=*/isSuperReceiver ? Loc : SourceLocation(), + Sel, Method, Loc, Loc, Loc, Args, + /*isImplicit=*/true); +} + +static void applyCocoaAPICheck(Sema &S, const ObjCMessageExpr *Msg, + unsigned DiagID, + bool (*refactor)(const ObjCMessageExpr *, + const NSAPI &, edit::Commit &)) { + SourceLocation MsgLoc = Msg->getExprLoc(); + if (S.Diags.isIgnored(DiagID, MsgLoc)) + return; + + SourceManager &SM = S.SourceMgr; + edit::Commit ECommit(SM, S.LangOpts); + if (refactor(Msg,*S.NSAPIObj, ECommit)) { + auto Builder = S.Diag(MsgLoc, DiagID) + << Msg->getSelector() << Msg->getSourceRange(); + // FIXME: Don't emit diagnostic at all if fixits are non-commitable. + if (!ECommit.isCommitable()) + return; + for (edit::Commit::edit_iterator + I = ECommit.edit_begin(), E = ECommit.edit_end(); I != E; ++I) { + const edit::Commit::Edit &Edit = *I; + switch (Edit.Kind) { + case edit::Commit::Act_Insert: + Builder.AddFixItHint(FixItHint::CreateInsertion(Edit.OrigLoc, + Edit.Text, + Edit.BeforePrev)); + break; + case edit::Commit::Act_InsertFromRange: + Builder.AddFixItHint( + FixItHint::CreateInsertionFromRange(Edit.OrigLoc, + Edit.getInsertFromRange(SM), + Edit.BeforePrev)); + break; + case edit::Commit::Act_Remove: + Builder.AddFixItHint(FixItHint::CreateRemoval(Edit.getFileRange(SM))); + break; + } + } + } +} + +static void checkCocoaAPI(Sema &S, const ObjCMessageExpr *Msg) { + applyCocoaAPICheck(S, Msg, diag::warn_objc_redundant_literal_use, + edit::rewriteObjCRedundantCallWithLiteral); +} + +static void checkFoundationAPI(Sema &S, SourceLocation Loc, + const ObjCMethodDecl *Method, + ArrayRef<Expr *> Args, QualType ReceiverType, + bool IsClassObjectCall) { + // Check if this is a performSelector method that uses a selector that returns + // a record or a vector type. + if (Method->getSelector().getMethodFamily() != OMF_performSelector || + Args.empty()) + return; + const auto *SE = dyn_cast<ObjCSelectorExpr>(Args[0]->IgnoreParens()); + if (!SE) + return; + ObjCMethodDecl *ImpliedMethod; + if (!IsClassObjectCall) { + const auto *OPT = ReceiverType->getAs<ObjCObjectPointerType>(); + if (!OPT || !OPT->getInterfaceDecl()) + return; + ImpliedMethod = + OPT->getInterfaceDecl()->lookupInstanceMethod(SE->getSelector()); + if (!ImpliedMethod) + ImpliedMethod = + OPT->getInterfaceDecl()->lookupPrivateMethod(SE->getSelector()); + } else { + const auto *IT = ReceiverType->getAs<ObjCInterfaceType>(); + if (!IT) + return; + ImpliedMethod = IT->getDecl()->lookupClassMethod(SE->getSelector()); + if (!ImpliedMethod) + ImpliedMethod = + IT->getDecl()->lookupPrivateClassMethod(SE->getSelector()); + } + if (!ImpliedMethod) + return; + QualType Ret = ImpliedMethod->getReturnType(); + if (Ret->isRecordType() || Ret->isVectorType() || Ret->isExtVectorType()) { + S.Diag(Loc, diag::warn_objc_unsafe_perform_selector) + << Method->getSelector() + << (!Ret->isRecordType() + ? /*Vector*/ 2 + : Ret->isUnionType() ? /*Union*/ 1 : /*Struct*/ 0); + S.Diag(ImpliedMethod->getBeginLoc(), + diag::note_objc_unsafe_perform_selector_method_declared_here) + << ImpliedMethod->getSelector() << Ret; + } +} + +/// Diagnose use of %s directive in an NSString which is being passed +/// as formatting string to formatting method. +static void +DiagnoseCStringFormatDirectiveInObjCAPI(Sema &S, + ObjCMethodDecl *Method, + Selector Sel, + Expr **Args, unsigned NumArgs) { + unsigned Idx = 0; + bool Format = false; + ObjCStringFormatFamily SFFamily = Sel.getStringFormatFamily(); + if (SFFamily == ObjCStringFormatFamily::SFF_NSString) { + Idx = 0; + Format = true; + } + else if (Method) { + for (const auto *I : Method->specific_attrs<FormatAttr>()) { + if (S.GetFormatNSStringIdx(I, Idx)) { + Format = true; + break; + } + } + } + if (!Format || NumArgs <= Idx) + return; + + Expr *FormatExpr = Args[Idx]; + if (ObjCStringLiteral *OSL = + dyn_cast<ObjCStringLiteral>(FormatExpr->IgnoreParenImpCasts())) { + StringLiteral *FormatString = OSL->getString(); + if (S.FormatStringHasSArg(FormatString)) { + S.Diag(FormatExpr->getExprLoc(), diag::warn_objc_cdirective_format_string) + << "%s" << 0 << 0; + if (Method) + S.Diag(Method->getLocation(), diag::note_method_declared_at) + << Method->getDeclName(); + } + } +} + +/// Build an Objective-C class message expression. +/// +/// This routine takes care of both normal class messages and +/// class messages to the superclass. +/// +/// \param ReceiverTypeInfo Type source information that describes the +/// receiver of this message. This may be NULL, in which case we are +/// sending to the superclass and \p SuperLoc must be a valid source +/// location. + +/// \param ReceiverType The type of the object receiving the +/// message. When \p ReceiverTypeInfo is non-NULL, this is the same +/// type as that refers to. For a superclass send, this is the type of +/// the superclass. +/// +/// \param SuperLoc The location of the "super" keyword in a +/// superclass message. +/// +/// \param Sel The selector to which the message is being sent. +/// +/// \param Method The method that this class message is invoking, if +/// already known. +/// +/// \param LBracLoc The location of the opening square bracket ']'. +/// +/// \param RBracLoc The location of the closing square bracket ']'. +/// +/// \param ArgsIn The message arguments. +ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, + QualType ReceiverType, + SourceLocation SuperLoc, + Selector Sel, + ObjCMethodDecl *Method, + SourceLocation LBracLoc, + ArrayRef<SourceLocation> SelectorLocs, + SourceLocation RBracLoc, + MultiExprArg ArgsIn, + bool isImplicit) { + SourceLocation Loc = SuperLoc.isValid()? SuperLoc + : ReceiverTypeInfo->getTypeLoc().getSourceRange().getBegin(); + if (LBracLoc.isInvalid()) { + Diag(Loc, diag::err_missing_open_square_message_send) + << FixItHint::CreateInsertion(Loc, "["); + LBracLoc = Loc; + } + ArrayRef<SourceLocation> SelectorSlotLocs; + if (!SelectorLocs.empty() && SelectorLocs.front().isValid()) + SelectorSlotLocs = SelectorLocs; + else + SelectorSlotLocs = Loc; + SourceLocation SelLoc = SelectorSlotLocs.front(); + + if (ReceiverType->isDependentType()) { + // If the receiver type is dependent, we can't type-check anything + // at this point. Build a dependent expression. + unsigned NumArgs = ArgsIn.size(); + Expr **Args = ArgsIn.data(); + assert(SuperLoc.isInvalid() && "Message to super with dependent type"); + return ObjCMessageExpr::Create( + Context, ReceiverType, VK_PRValue, LBracLoc, ReceiverTypeInfo, Sel, + SelectorLocs, /*Method=*/nullptr, makeArrayRef(Args, NumArgs), RBracLoc, + isImplicit); + } + + // Find the class to which we are sending this message. + ObjCInterfaceDecl *Class = nullptr; + const ObjCObjectType *ClassType = ReceiverType->getAs<ObjCObjectType>(); + if (!ClassType || !(Class = ClassType->getInterface())) { + Diag(Loc, diag::err_invalid_receiver_class_message) + << ReceiverType; + return ExprError(); + } + assert(Class && "We don't know which class we're messaging?"); + // objc++ diagnoses during typename annotation. + if (!getLangOpts().CPlusPlus) + (void)DiagnoseUseOfDecl(Class, SelectorSlotLocs); + // Find the method we are messaging. + if (!Method) { + SourceRange TypeRange + = SuperLoc.isValid()? SourceRange(SuperLoc) + : ReceiverTypeInfo->getTypeLoc().getSourceRange(); + if (RequireCompleteType(Loc, Context.getObjCInterfaceType(Class), + (getLangOpts().ObjCAutoRefCount + ? diag::err_arc_receiver_forward_class + : diag::warn_receiver_forward_class), + TypeRange)) { + // A forward class used in messaging is treated as a 'Class' + Method = LookupFactoryMethodInGlobalPool(Sel, + SourceRange(LBracLoc, RBracLoc)); + if (Method && !getLangOpts().ObjCAutoRefCount) + Diag(Method->getLocation(), diag::note_method_sent_forward_class) + << Method->getDeclName(); + } + if (!Method) + Method = Class->lookupClassMethod(Sel); + + // If we have an implementation in scope, check "private" methods. + if (!Method) + Method = Class->lookupPrivateClassMethod(Sel); + + if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs, + nullptr, false, false, Class)) + return ExprError(); + } + + // Check the argument types and determine the result type. + QualType ReturnType; + ExprValueKind VK = VK_PRValue; + + unsigned NumArgs = ArgsIn.size(); + Expr **Args = ArgsIn.data(); + if (CheckMessageArgumentTypes(/*Receiver=*/nullptr, ReceiverType, + MultiExprArg(Args, NumArgs), Sel, SelectorLocs, + Method, true, SuperLoc.isValid(), LBracLoc, + RBracLoc, SourceRange(), ReturnType, VK)) + return ExprError(); + + if (Method && !Method->getReturnType()->isVoidType() && + RequireCompleteType(LBracLoc, Method->getReturnType(), + diag::err_illegal_message_expr_incomplete_type)) + return ExprError(); + + if (Method && Method->isDirectMethod() && SuperLoc.isValid()) { + Diag(SuperLoc, diag::err_messaging_super_with_direct_method) + << FixItHint::CreateReplacement( + SuperLoc, getLangOpts().ObjCAutoRefCount + ? "self" + : Method->getClassInterface()->getName()); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + + // Warn about explicit call of +initialize on its own class. But not on 'super'. + if (Method && Method->getMethodFamily() == OMF_initialize) { + if (!SuperLoc.isValid()) { + const ObjCInterfaceDecl *ID = + dyn_cast<ObjCInterfaceDecl>(Method->getDeclContext()); + if (ID == Class) { + Diag(Loc, diag::warn_direct_initialize_call); + Diag(Method->getLocation(), diag::note_method_declared_at) + << Method->getDeclName(); + } + } + else if (ObjCMethodDecl *CurMeth = getCurMethodDecl()) { + // [super initialize] is allowed only within an +initialize implementation + if (CurMeth->getMethodFamily() != OMF_initialize) { + Diag(Loc, diag::warn_direct_super_initialize_call); + Diag(Method->getLocation(), diag::note_method_declared_at) + << Method->getDeclName(); + Diag(CurMeth->getLocation(), diag::note_method_declared_at) + << CurMeth->getDeclName(); + } + } + } + + DiagnoseCStringFormatDirectiveInObjCAPI(*this, Method, Sel, Args, NumArgs); + + // Construct the appropriate ObjCMessageExpr. + ObjCMessageExpr *Result; + if (SuperLoc.isValid()) + Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc, + SuperLoc, /*IsInstanceSuper=*/false, + ReceiverType, Sel, SelectorLocs, + Method, makeArrayRef(Args, NumArgs), + RBracLoc, isImplicit); + else { + Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc, + ReceiverTypeInfo, Sel, SelectorLocs, + Method, makeArrayRef(Args, NumArgs), + RBracLoc, isImplicit); + if (!isImplicit) + checkCocoaAPI(*this, Result); + } + if (Method) + checkFoundationAPI(*this, SelLoc, Method, makeArrayRef(Args, NumArgs), + ReceiverType, /*IsClassObjectCall=*/true); + return MaybeBindToTemporary(Result); +} + +// ActOnClassMessage - used for both unary and keyword messages. +// ArgExprs is optional - if it is present, the number of expressions +// is obtained from Sel.getNumArgs(). +ExprResult Sema::ActOnClassMessage(Scope *S, + ParsedType Receiver, + Selector Sel, + SourceLocation LBracLoc, + ArrayRef<SourceLocation> SelectorLocs, + SourceLocation RBracLoc, + MultiExprArg Args) { + TypeSourceInfo *ReceiverTypeInfo; + QualType ReceiverType = GetTypeFromParser(Receiver, &ReceiverTypeInfo); + if (ReceiverType.isNull()) + return ExprError(); + + if (!ReceiverTypeInfo) + ReceiverTypeInfo = Context.getTrivialTypeSourceInfo(ReceiverType, LBracLoc); + + return BuildClassMessage(ReceiverTypeInfo, ReceiverType, + /*SuperLoc=*/SourceLocation(), Sel, + /*Method=*/nullptr, LBracLoc, SelectorLocs, RBracLoc, + Args); +} + +ExprResult Sema::BuildInstanceMessageImplicit(Expr *Receiver, + QualType ReceiverType, + SourceLocation Loc, + Selector Sel, + ObjCMethodDecl *Method, + MultiExprArg Args) { + return BuildInstanceMessage(Receiver, ReceiverType, + /*SuperLoc=*/!Receiver ? Loc : SourceLocation(), + Sel, Method, Loc, Loc, Loc, Args, + /*isImplicit=*/true); +} + +static bool isMethodDeclaredInRootProtocol(Sema &S, const ObjCMethodDecl *M) { + if (!S.NSAPIObj) + return false; + const auto *Protocol = dyn_cast<ObjCProtocolDecl>(M->getDeclContext()); + if (!Protocol) + return false; + const IdentifierInfo *II = S.NSAPIObj->getNSClassId(NSAPI::ClassId_NSObject); + if (const auto *RootClass = dyn_cast_or_null<ObjCInterfaceDecl>( + S.LookupSingleName(S.TUScope, II, Protocol->getBeginLoc(), + Sema::LookupOrdinaryName))) { + for (const ObjCProtocolDecl *P : RootClass->all_referenced_protocols()) { + if (P->getCanonicalDecl() == Protocol->getCanonicalDecl()) + return true; + } + } + return false; +} + +/// Build an Objective-C instance message expression. +/// +/// This routine takes care of both normal instance messages and +/// instance messages to the superclass instance. +/// +/// \param Receiver The expression that computes the object that will +/// receive this message. This may be empty, in which case we are +/// sending to the superclass instance and \p SuperLoc must be a valid +/// source location. +/// +/// \param ReceiverType The (static) type of the object receiving the +/// message. When a \p Receiver expression is provided, this is the +/// same type as that expression. For a superclass instance send, this +/// is a pointer to the type of the superclass. +/// +/// \param SuperLoc The location of the "super" keyword in a +/// superclass instance message. +/// +/// \param Sel The selector to which the message is being sent. +/// +/// \param Method The method that this instance message is invoking, if +/// already known. +/// +/// \param LBracLoc The location of the opening square bracket ']'. +/// +/// \param RBracLoc The location of the closing square bracket ']'. +/// +/// \param ArgsIn The message arguments. +ExprResult Sema::BuildInstanceMessage(Expr *Receiver, + QualType ReceiverType, + SourceLocation SuperLoc, + Selector Sel, + ObjCMethodDecl *Method, + SourceLocation LBracLoc, + ArrayRef<SourceLocation> SelectorLocs, + SourceLocation RBracLoc, + MultiExprArg ArgsIn, + bool isImplicit) { + assert((Receiver || SuperLoc.isValid()) && "If the Receiver is null, the " + "SuperLoc must be valid so we can " + "use it instead."); + + // The location of the receiver. + SourceLocation Loc = SuperLoc.isValid() ? SuperLoc : Receiver->getBeginLoc(); + SourceRange RecRange = + SuperLoc.isValid()? SuperLoc : Receiver->getSourceRange(); + ArrayRef<SourceLocation> SelectorSlotLocs; + if (!SelectorLocs.empty() && SelectorLocs.front().isValid()) + SelectorSlotLocs = SelectorLocs; + else + SelectorSlotLocs = Loc; + SourceLocation SelLoc = SelectorSlotLocs.front(); + + if (LBracLoc.isInvalid()) { + Diag(Loc, diag::err_missing_open_square_message_send) + << FixItHint::CreateInsertion(Loc, "["); + LBracLoc = Loc; + } + + // If we have a receiver expression, perform appropriate promotions + // and determine receiver type. + if (Receiver) { + if (Receiver->hasPlaceholderType()) { + ExprResult Result; + if (Receiver->getType() == Context.UnknownAnyTy) + Result = forceUnknownAnyToType(Receiver, Context.getObjCIdType()); + else + Result = CheckPlaceholderExpr(Receiver); + if (Result.isInvalid()) return ExprError(); + Receiver = Result.get(); + } + + if (Receiver->isTypeDependent()) { + // If the receiver is type-dependent, we can't type-check anything + // at this point. Build a dependent expression. + unsigned NumArgs = ArgsIn.size(); + Expr **Args = ArgsIn.data(); + assert(SuperLoc.isInvalid() && "Message to super with dependent type"); + return ObjCMessageExpr::Create( + Context, Context.DependentTy, VK_PRValue, LBracLoc, Receiver, Sel, + SelectorLocs, /*Method=*/nullptr, makeArrayRef(Args, NumArgs), + RBracLoc, isImplicit); + } + + // If necessary, apply function/array conversion to the receiver. + // C99 6.7.5.3p[7,8]. + ExprResult Result = DefaultFunctionArrayLvalueConversion(Receiver); + if (Result.isInvalid()) + return ExprError(); + Receiver = Result.get(); + ReceiverType = Receiver->getType(); + + // If the receiver is an ObjC pointer, a block pointer, or an + // __attribute__((NSObject)) pointer, we don't need to do any + // special conversion in order to look up a receiver. + if (ReceiverType->isObjCRetainableType()) { + // do nothing + } else if (!getLangOpts().ObjCAutoRefCount && + !Context.getObjCIdType().isNull() && + (ReceiverType->isPointerType() || + ReceiverType->isIntegerType())) { + // Implicitly convert integers and pointers to 'id' but emit a warning. + // But not in ARC. + Diag(Loc, diag::warn_bad_receiver_type) << ReceiverType << RecRange; + if (ReceiverType->isPointerType()) { + Receiver = ImpCastExprToType(Receiver, Context.getObjCIdType(), + CK_CPointerToObjCPointerCast).get(); + } else { + // TODO: specialized warning on null receivers? + bool IsNull = Receiver->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNull); + CastKind Kind = IsNull ? CK_NullToPointer : CK_IntegralToPointer; + Receiver = ImpCastExprToType(Receiver, Context.getObjCIdType(), + Kind).get(); + } + ReceiverType = Receiver->getType(); + } else if (getLangOpts().CPlusPlus) { + // The receiver must be a complete type. + if (RequireCompleteType(Loc, Receiver->getType(), + diag::err_incomplete_receiver_type)) + return ExprError(); + + ExprResult result = PerformContextuallyConvertToObjCPointer(Receiver); + if (result.isUsable()) { + Receiver = result.get(); + ReceiverType = Receiver->getType(); + } + } + } + + // There's a somewhat weird interaction here where we assume that we + // won't actually have a method unless we also don't need to do some + // of the more detailed type-checking on the receiver. + + if (!Method) { + // Handle messages to id and __kindof types (where we use the + // global method pool). + const ObjCObjectType *typeBound = nullptr; + bool receiverIsIdLike = ReceiverType->isObjCIdOrObjectKindOfType(Context, + typeBound); + if (receiverIsIdLike || ReceiverType->isBlockPointerType() || + (Receiver && Context.isObjCNSObjectType(Receiver->getType()))) { + SmallVector<ObjCMethodDecl*, 4> Methods; + // If we have a type bound, further filter the methods. + CollectMultipleMethodsInGlobalPool(Sel, Methods, true/*InstanceFirst*/, + true/*CheckTheOther*/, typeBound); + if (!Methods.empty()) { + // We choose the first method as the initial candidate, then try to + // select a better one. + Method = Methods[0]; + + if (ObjCMethodDecl *BestMethod = + SelectBestMethod(Sel, ArgsIn, Method->isInstanceMethod(), Methods)) + Method = BestMethod; + + if (!AreMultipleMethodsInGlobalPool(Sel, Method, + SourceRange(LBracLoc, RBracLoc), + receiverIsIdLike, Methods)) + DiagnoseUseOfDecl(Method, SelectorSlotLocs); + } + } else if (ReceiverType->isObjCClassOrClassKindOfType() || + ReceiverType->isObjCQualifiedClassType()) { + // Handle messages to Class. + // We allow sending a message to a qualified Class ("Class<foo>"), which + // is ok as long as one of the protocols implements the selector (if not, + // warn). + if (!ReceiverType->isObjCClassOrClassKindOfType()) { + const ObjCObjectPointerType *QClassTy + = ReceiverType->getAsObjCQualifiedClassType(); + // Search protocols for class methods. + Method = LookupMethodInQualifiedType(Sel, QClassTy, false); + if (!Method) { + Method = LookupMethodInQualifiedType(Sel, QClassTy, true); + // warn if instance method found for a Class message. + if (Method && !isMethodDeclaredInRootProtocol(*this, Method)) { + Diag(SelLoc, diag::warn_instance_method_on_class_found) + << Method->getSelector() << Sel; + Diag(Method->getLocation(), diag::note_method_declared_at) + << Method->getDeclName(); + } + } + } else { + if (ObjCMethodDecl *CurMeth = getCurMethodDecl()) { + if (ObjCInterfaceDecl *ClassDecl = CurMeth->getClassInterface()) { + // As a guess, try looking for the method in the current interface. + // This very well may not produce the "right" method. + + // First check the public methods in the class interface. + Method = ClassDecl->lookupClassMethod(Sel); + + if (!Method) + Method = ClassDecl->lookupPrivateClassMethod(Sel); + + if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs)) + return ExprError(); + } + } + if (!Method) { + // If not messaging 'self', look for any factory method named 'Sel'. + if (!Receiver || !isSelfExpr(Receiver)) { + // If no class (factory) method was found, check if an _instance_ + // method of the same name exists in the root class only. + SmallVector<ObjCMethodDecl*, 4> Methods; + CollectMultipleMethodsInGlobalPool(Sel, Methods, + false/*InstanceFirst*/, + true/*CheckTheOther*/); + if (!Methods.empty()) { + // We choose the first method as the initial candidate, then try + // to select a better one. + Method = Methods[0]; + + // If we find an instance method, emit warning. + if (Method->isInstanceMethod()) { + if (const ObjCInterfaceDecl *ID = + dyn_cast<ObjCInterfaceDecl>(Method->getDeclContext())) { + if (ID->getSuperClass()) + Diag(SelLoc, diag::warn_root_inst_method_not_found) + << Sel << SourceRange(LBracLoc, RBracLoc); + } + } + + if (ObjCMethodDecl *BestMethod = + SelectBestMethod(Sel, ArgsIn, Method->isInstanceMethod(), + Methods)) + Method = BestMethod; + } + } + } + } + } else { + ObjCInterfaceDecl *ClassDecl = nullptr; + + // We allow sending a message to a qualified ID ("id<foo>"), which is ok as + // long as one of the protocols implements the selector (if not, warn). + // And as long as message is not deprecated/unavailable (warn if it is). + if (const ObjCObjectPointerType *QIdTy + = ReceiverType->getAsObjCQualifiedIdType()) { + // Search protocols for instance methods. + Method = LookupMethodInQualifiedType(Sel, QIdTy, true); + if (!Method) + Method = LookupMethodInQualifiedType(Sel, QIdTy, false); + if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs)) + return ExprError(); + } else if (const ObjCObjectPointerType *OCIType + = ReceiverType->getAsObjCInterfacePointerType()) { + // We allow sending a message to a pointer to an interface (an object). + ClassDecl = OCIType->getInterfaceDecl(); + + // Try to complete the type. Under ARC, this is a hard error from which + // we don't try to recover. + // FIXME: In the non-ARC case, this will still be a hard error if the + // definition is found in a module that's not visible. + const ObjCInterfaceDecl *forwardClass = nullptr; + if (RequireCompleteType(Loc, OCIType->getPointeeType(), + getLangOpts().ObjCAutoRefCount + ? diag::err_arc_receiver_forward_instance + : diag::warn_receiver_forward_instance, + RecRange)) { + if (getLangOpts().ObjCAutoRefCount) + return ExprError(); + + forwardClass = OCIType->getInterfaceDecl(); + Diag(Receiver ? Receiver->getBeginLoc() : SuperLoc, + diag::note_receiver_is_id); + Method = nullptr; + } else { + Method = ClassDecl->lookupInstanceMethod(Sel); + } + + if (!Method) + // Search protocol qualifiers. + Method = LookupMethodInQualifiedType(Sel, OCIType, true); + + if (!Method) { + // If we have implementations in scope, check "private" methods. + Method = ClassDecl->lookupPrivateMethod(Sel); + + if (!Method && getLangOpts().ObjCAutoRefCount) { + Diag(SelLoc, diag::err_arc_may_not_respond) + << OCIType->getPointeeType() << Sel << RecRange + << SourceRange(SelectorLocs.front(), SelectorLocs.back()); + return ExprError(); + } + + if (!Method && (!Receiver || !isSelfExpr(Receiver))) { + // If we still haven't found a method, look in the global pool. This + // behavior isn't very desirable, however we need it for GCC + // compatibility. FIXME: should we deviate?? + if (OCIType->qual_empty()) { + SmallVector<ObjCMethodDecl*, 4> Methods; + CollectMultipleMethodsInGlobalPool(Sel, Methods, + true/*InstanceFirst*/, + false/*CheckTheOther*/); + if (!Methods.empty()) { + // We choose the first method as the initial candidate, then try + // to select a better one. + Method = Methods[0]; + + if (ObjCMethodDecl *BestMethod = + SelectBestMethod(Sel, ArgsIn, Method->isInstanceMethod(), + Methods)) + Method = BestMethod; + + AreMultipleMethodsInGlobalPool(Sel, Method, + SourceRange(LBracLoc, RBracLoc), + true/*receiverIdOrClass*/, + Methods); + } + if (Method && !forwardClass) + Diag(SelLoc, diag::warn_maynot_respond) + << OCIType->getInterfaceDecl()->getIdentifier() + << Sel << RecRange; + } + } + } + if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs, forwardClass)) + return ExprError(); + } else { + // Reject other random receiver types (e.g. structs). + Diag(Loc, diag::err_bad_receiver_type) << ReceiverType << RecRange; + return ExprError(); + } + } + } + + FunctionScopeInfo *DIFunctionScopeInfo = + (Method && Method->getMethodFamily() == OMF_init) + ? getEnclosingFunction() : nullptr; + + if (Method && Method->isDirectMethod()) { + if (ReceiverType->isObjCIdType() && !isImplicit) { + Diag(Receiver->getExprLoc(), + diag::err_messaging_unqualified_id_with_direct_method); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + + // Under ARC, self can't be assigned, and doing a direct call to `self` + // when it's a Class is hence safe. For other cases, we can't trust `self` + // is what we think it is, so we reject it. + if (ReceiverType->isObjCClassType() && !isImplicit && + !(Receiver->isObjCSelfExpr() && getLangOpts().ObjCAutoRefCount)) { + { + auto Builder = Diag(Receiver->getExprLoc(), + diag::err_messaging_class_with_direct_method); + if (Receiver->isObjCSelfExpr()) { + Builder.AddFixItHint(FixItHint::CreateReplacement( + RecRange, Method->getClassInterface()->getName())); + } + } + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + + if (SuperLoc.isValid()) { + { + auto Builder = + Diag(SuperLoc, diag::err_messaging_super_with_direct_method); + if (ReceiverType->isObjCClassType()) { + Builder.AddFixItHint(FixItHint::CreateReplacement( + SuperLoc, Method->getClassInterface()->getName())); + } else { + Builder.AddFixItHint(FixItHint::CreateReplacement(SuperLoc, "self")); + } + } + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + } else if (ReceiverType->isObjCIdType() && !isImplicit) { + Diag(Receiver->getExprLoc(), diag::warn_messaging_unqualified_id); + } + + if (DIFunctionScopeInfo && + DIFunctionScopeInfo->ObjCIsDesignatedInit && + (SuperLoc.isValid() || isSelfExpr(Receiver))) { + bool isDesignatedInitChain = false; + if (SuperLoc.isValid()) { + if (const ObjCObjectPointerType * + OCIType = ReceiverType->getAsObjCInterfacePointerType()) { + if (const ObjCInterfaceDecl *ID = OCIType->getInterfaceDecl()) { + // Either we know this is a designated initializer or we + // conservatively assume it because we don't know for sure. + if (!ID->declaresOrInheritsDesignatedInitializers() || + ID->isDesignatedInitializer(Sel)) { + isDesignatedInitChain = true; + DIFunctionScopeInfo->ObjCWarnForNoDesignatedInitChain = false; + } + } + } + } + if (!isDesignatedInitChain) { + const ObjCMethodDecl *InitMethod = nullptr; + bool isDesignated = + getCurMethodDecl()->isDesignatedInitializerForTheInterface(&InitMethod); + assert(isDesignated && InitMethod); + (void)isDesignated; + Diag(SelLoc, SuperLoc.isValid() ? + diag::warn_objc_designated_init_non_designated_init_call : + diag::warn_objc_designated_init_non_super_designated_init_call); + Diag(InitMethod->getLocation(), + diag::note_objc_designated_init_marked_here); + } + } + + if (DIFunctionScopeInfo && + DIFunctionScopeInfo->ObjCIsSecondaryInit && + (SuperLoc.isValid() || isSelfExpr(Receiver))) { + if (SuperLoc.isValid()) { + Diag(SelLoc, diag::warn_objc_secondary_init_super_init_call); + } else { + DIFunctionScopeInfo->ObjCWarnForNoInitDelegation = false; + } + } + + // Check the message arguments. + unsigned NumArgs = ArgsIn.size(); + Expr **Args = ArgsIn.data(); + QualType ReturnType; + ExprValueKind VK = VK_PRValue; + bool ClassMessage = (ReceiverType->isObjCClassType() || + ReceiverType->isObjCQualifiedClassType()); + if (CheckMessageArgumentTypes(Receiver, ReceiverType, + MultiExprArg(Args, NumArgs), Sel, SelectorLocs, + Method, ClassMessage, SuperLoc.isValid(), + LBracLoc, RBracLoc, RecRange, ReturnType, VK)) + return ExprError(); + + if (Method && !Method->getReturnType()->isVoidType() && + RequireCompleteType(LBracLoc, Method->getReturnType(), + diag::err_illegal_message_expr_incomplete_type)) + return ExprError(); + + // In ARC, forbid the user from sending messages to + // retain/release/autorelease/dealloc/retainCount explicitly. + if (getLangOpts().ObjCAutoRefCount) { + ObjCMethodFamily family = + (Method ? Method->getMethodFamily() : Sel.getMethodFamily()); + switch (family) { + case OMF_init: + if (Method) + checkInitMethod(Method, ReceiverType); + break; + + case OMF_None: + case OMF_alloc: + case OMF_copy: + case OMF_finalize: + case OMF_mutableCopy: + case OMF_new: + case OMF_self: + case OMF_initialize: + break; + + case OMF_dealloc: + case OMF_retain: + case OMF_release: + case OMF_autorelease: + case OMF_retainCount: + Diag(SelLoc, diag::err_arc_illegal_explicit_message) + << Sel << RecRange; + break; + + case OMF_performSelector: + if (Method && NumArgs >= 1) { + if (const auto *SelExp = + dyn_cast<ObjCSelectorExpr>(Args[0]->IgnoreParens())) { + Selector ArgSel = SelExp->getSelector(); + ObjCMethodDecl *SelMethod = + LookupInstanceMethodInGlobalPool(ArgSel, + SelExp->getSourceRange()); + if (!SelMethod) + SelMethod = + LookupFactoryMethodInGlobalPool(ArgSel, + SelExp->getSourceRange()); + if (SelMethod) { + ObjCMethodFamily SelFamily = SelMethod->getMethodFamily(); + switch (SelFamily) { + case OMF_alloc: + case OMF_copy: + case OMF_mutableCopy: + case OMF_new: + case OMF_init: + // Issue error, unless ns_returns_not_retained. + if (!SelMethod->hasAttr<NSReturnsNotRetainedAttr>()) { + // selector names a +1 method + Diag(SelLoc, + diag::err_arc_perform_selector_retains); + Diag(SelMethod->getLocation(), diag::note_method_declared_at) + << SelMethod->getDeclName(); + } + break; + default: + // +0 call. OK. unless ns_returns_retained. + if (SelMethod->hasAttr<NSReturnsRetainedAttr>()) { + // selector names a +1 method + Diag(SelLoc, + diag::err_arc_perform_selector_retains); + Diag(SelMethod->getLocation(), diag::note_method_declared_at) + << SelMethod->getDeclName(); + } + break; + } + } + } else { + // error (may leak). + Diag(SelLoc, diag::warn_arc_perform_selector_leaks); + Diag(Args[0]->getExprLoc(), diag::note_used_here); + } + } + break; + } + } + + DiagnoseCStringFormatDirectiveInObjCAPI(*this, Method, Sel, Args, NumArgs); + + // Construct the appropriate ObjCMessageExpr instance. + ObjCMessageExpr *Result; + if (SuperLoc.isValid()) + Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc, + SuperLoc, /*IsInstanceSuper=*/true, + ReceiverType, Sel, SelectorLocs, Method, + makeArrayRef(Args, NumArgs), RBracLoc, + isImplicit); + else { + Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc, + Receiver, Sel, SelectorLocs, Method, + makeArrayRef(Args, NumArgs), RBracLoc, + isImplicit); + if (!isImplicit) + checkCocoaAPI(*this, Result); + } + if (Method) { + bool IsClassObjectCall = ClassMessage; + // 'self' message receivers in class methods should be treated as message + // sends to the class object in order for the semantic checks to be + // performed correctly. Messages to 'super' already count as class messages, + // so they don't need to be handled here. + if (Receiver && isSelfExpr(Receiver)) { + if (const auto *OPT = ReceiverType->getAs<ObjCObjectPointerType>()) { + if (OPT->getObjectType()->isObjCClass()) { + if (const auto *CurMeth = getCurMethodDecl()) { + IsClassObjectCall = true; + ReceiverType = + Context.getObjCInterfaceType(CurMeth->getClassInterface()); + } + } + } + } + checkFoundationAPI(*this, SelLoc, Method, makeArrayRef(Args, NumArgs), + ReceiverType, IsClassObjectCall); + } + + if (getLangOpts().ObjCAutoRefCount) { + // In ARC, annotate delegate init calls. + if (Result->getMethodFamily() == OMF_init && + (SuperLoc.isValid() || isSelfExpr(Receiver))) { + // Only consider init calls *directly* in init implementations, + // not within blocks. + ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(CurContext); + if (method && method->getMethodFamily() == OMF_init) { + // The implicit assignment to self means we also don't want to + // consume the result. + Result->setDelegateInitCall(true); + return Result; + } + } + + // In ARC, check for message sends which are likely to introduce + // retain cycles. + checkRetainCycles(Result); + } + + if (getLangOpts().ObjCWeak) { + if (!isImplicit && Method) { + if (const ObjCPropertyDecl *Prop = Method->findPropertyDecl()) { + bool IsWeak = + Prop->getPropertyAttributes() & ObjCPropertyAttribute::kind_weak; + if (!IsWeak && Sel.isUnarySelector()) + IsWeak = ReturnType.getObjCLifetime() & Qualifiers::OCL_Weak; + if (IsWeak && !isUnevaluatedContext() && + !Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, LBracLoc)) + getCurFunction()->recordUseOfWeak(Result, Prop); + } + } + } + + CheckObjCCircularContainer(Result); + + return MaybeBindToTemporary(Result); +} + +static void RemoveSelectorFromWarningCache(Sema &S, Expr* Arg) { + if (ObjCSelectorExpr *OSE = + dyn_cast<ObjCSelectorExpr>(Arg->IgnoreParenCasts())) { + Selector Sel = OSE->getSelector(); + SourceLocation Loc = OSE->getAtLoc(); + auto Pos = S.ReferencedSelectors.find(Sel); + if (Pos != S.ReferencedSelectors.end() && Pos->second == Loc) + S.ReferencedSelectors.erase(Pos); + } +} + +// ActOnInstanceMessage - used for both unary and keyword messages. +// ArgExprs is optional - if it is present, the number of expressions +// is obtained from Sel.getNumArgs(). +ExprResult Sema::ActOnInstanceMessage(Scope *S, + Expr *Receiver, + Selector Sel, + SourceLocation LBracLoc, + ArrayRef<SourceLocation> SelectorLocs, + SourceLocation RBracLoc, + MultiExprArg Args) { + if (!Receiver) + return ExprError(); + + // A ParenListExpr can show up while doing error recovery with invalid code. + if (isa<ParenListExpr>(Receiver)) { + ExprResult Result = MaybeConvertParenListExprToParenExpr(S, Receiver); + if (Result.isInvalid()) return ExprError(); + Receiver = Result.get(); + } + + if (RespondsToSelectorSel.isNull()) { + IdentifierInfo *SelectorId = &Context.Idents.get("respondsToSelector"); + RespondsToSelectorSel = Context.Selectors.getUnarySelector(SelectorId); + } + if (Sel == RespondsToSelectorSel) + RemoveSelectorFromWarningCache(*this, Args[0]); + + return BuildInstanceMessage(Receiver, Receiver->getType(), + /*SuperLoc=*/SourceLocation(), Sel, + /*Method=*/nullptr, LBracLoc, SelectorLocs, + RBracLoc, Args); +} + +enum ARCConversionTypeClass { + /// int, void, struct A + ACTC_none, + + /// id, void (^)() + ACTC_retainable, + + /// id*, id***, void (^*)(), + ACTC_indirectRetainable, + + /// void* might be a normal C type, or it might a CF type. + ACTC_voidPtr, + + /// struct A* + ACTC_coreFoundation +}; + +static bool isAnyRetainable(ARCConversionTypeClass ACTC) { + return (ACTC == ACTC_retainable || + ACTC == ACTC_coreFoundation || + ACTC == ACTC_voidPtr); +} + +static bool isAnyCLike(ARCConversionTypeClass ACTC) { + return ACTC == ACTC_none || + ACTC == ACTC_voidPtr || + ACTC == ACTC_coreFoundation; +} + +static ARCConversionTypeClass classifyTypeForARCConversion(QualType type) { + bool isIndirect = false; + + // Ignore an outermost reference type. + if (const ReferenceType *ref = type->getAs<ReferenceType>()) { + type = ref->getPointeeType(); + isIndirect = true; + } + + // Drill through pointers and arrays recursively. + while (true) { + if (const PointerType *ptr = type->getAs<PointerType>()) { + type = ptr->getPointeeType(); + + // The first level of pointer may be the innermost pointer on a CF type. + if (!isIndirect) { + if (type->isVoidType()) return ACTC_voidPtr; + if (type->isRecordType()) return ACTC_coreFoundation; + } + } else if (const ArrayType *array = type->getAsArrayTypeUnsafe()) { + type = QualType(array->getElementType()->getBaseElementTypeUnsafe(), 0); + } else { + break; + } + isIndirect = true; + } + + if (isIndirect) { + if (type->isObjCARCBridgableType()) + return ACTC_indirectRetainable; + return ACTC_none; + } + + if (type->isObjCARCBridgableType()) + return ACTC_retainable; + + return ACTC_none; +} + +namespace { + /// A result from the cast checker. + enum ACCResult { + /// Cannot be casted. + ACC_invalid, + + /// Can be safely retained or not retained. + ACC_bottom, + + /// Can be casted at +0. + ACC_plusZero, + + /// Can be casted at +1. + ACC_plusOne + }; + ACCResult merge(ACCResult left, ACCResult right) { + if (left == right) return left; + if (left == ACC_bottom) return right; + if (right == ACC_bottom) return left; + return ACC_invalid; + } + + /// A checker which white-lists certain expressions whose conversion + /// to or from retainable type would otherwise be forbidden in ARC. + class ARCCastChecker : public StmtVisitor<ARCCastChecker, ACCResult> { + typedef StmtVisitor<ARCCastChecker, ACCResult> super; + + ASTContext &Context; + ARCConversionTypeClass SourceClass; + ARCConversionTypeClass TargetClass; + bool Diagnose; + + static bool isCFType(QualType type) { + // Someday this can use ns_bridged. For now, it has to do this. + return type->isCARCBridgableType(); + } + + public: + ARCCastChecker(ASTContext &Context, ARCConversionTypeClass source, + ARCConversionTypeClass target, bool diagnose) + : Context(Context), SourceClass(source), TargetClass(target), + Diagnose(diagnose) {} + + using super::Visit; + ACCResult Visit(Expr *e) { + return super::Visit(e->IgnoreParens()); + } + + ACCResult VisitStmt(Stmt *s) { + return ACC_invalid; + } + + /// Null pointer constants can be casted however you please. + ACCResult VisitExpr(Expr *e) { + if (e->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull)) + return ACC_bottom; + return ACC_invalid; + } + + /// Objective-C string literals can be safely casted. + ACCResult VisitObjCStringLiteral(ObjCStringLiteral *e) { + // If we're casting to any retainable type, go ahead. Global + // strings are immune to retains, so this is bottom. + if (isAnyRetainable(TargetClass)) return ACC_bottom; + + return ACC_invalid; + } + + /// Look through certain implicit and explicit casts. + ACCResult VisitCastExpr(CastExpr *e) { + switch (e->getCastKind()) { + case CK_NullToPointer: + return ACC_bottom; + + case CK_NoOp: + case CK_LValueToRValue: + case CK_BitCast: + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + return Visit(e->getSubExpr()); + + default: + return ACC_invalid; + } + } + + /// Look through unary extension. + ACCResult VisitUnaryExtension(UnaryOperator *e) { + return Visit(e->getSubExpr()); + } + + /// Ignore the LHS of a comma operator. + ACCResult VisitBinComma(BinaryOperator *e) { + return Visit(e->getRHS()); + } + + /// Conditional operators are okay if both sides are okay. + ACCResult VisitConditionalOperator(ConditionalOperator *e) { + ACCResult left = Visit(e->getTrueExpr()); + if (left == ACC_invalid) return ACC_invalid; + return merge(left, Visit(e->getFalseExpr())); + } + + /// Look through pseudo-objects. + ACCResult VisitPseudoObjectExpr(PseudoObjectExpr *e) { + // If we're getting here, we should always have a result. + return Visit(e->getResultExpr()); + } + + /// Statement expressions are okay if their result expression is okay. + ACCResult VisitStmtExpr(StmtExpr *e) { + return Visit(e->getSubStmt()->body_back()); + } + + /// Some declaration references are okay. + ACCResult VisitDeclRefExpr(DeclRefExpr *e) { + VarDecl *var = dyn_cast<VarDecl>(e->getDecl()); + // References to global constants are okay. + if (isAnyRetainable(TargetClass) && + isAnyRetainable(SourceClass) && + var && + !var->hasDefinition(Context) && + var->getType().isConstQualified()) { + + // In system headers, they can also be assumed to be immune to retains. + // These are things like 'kCFStringTransformToLatin'. + if (Context.getSourceManager().isInSystemHeader(var->getLocation())) + return ACC_bottom; + + return ACC_plusZero; + } + + // Nothing else. + return ACC_invalid; + } + + /// Some calls are okay. + ACCResult VisitCallExpr(CallExpr *e) { + if (FunctionDecl *fn = e->getDirectCallee()) + if (ACCResult result = checkCallToFunction(fn)) + return result; + + return super::VisitCallExpr(e); + } + + ACCResult checkCallToFunction(FunctionDecl *fn) { + // Require a CF*Ref return type. + if (!isCFType(fn->getReturnType())) + return ACC_invalid; + + if (!isAnyRetainable(TargetClass)) + return ACC_invalid; + + // Honor an explicit 'not retained' attribute. + if (fn->hasAttr<CFReturnsNotRetainedAttr>()) + return ACC_plusZero; + + // Honor an explicit 'retained' attribute, except that for + // now we're not going to permit implicit handling of +1 results, + // because it's a bit frightening. + if (fn->hasAttr<CFReturnsRetainedAttr>()) + return Diagnose ? ACC_plusOne + : ACC_invalid; // ACC_plusOne if we start accepting this + + // Recognize this specific builtin function, which is used by CFSTR. + unsigned builtinID = fn->getBuiltinID(); + if (builtinID == Builtin::BI__builtin___CFStringMakeConstantString) + return ACC_bottom; + + // Otherwise, don't do anything implicit with an unaudited function. + if (!fn->hasAttr<CFAuditedTransferAttr>()) + return ACC_invalid; + + // Otherwise, it's +0 unless it follows the create convention. + if (ento::coreFoundation::followsCreateRule(fn)) + return Diagnose ? ACC_plusOne + : ACC_invalid; // ACC_plusOne if we start accepting this + + return ACC_plusZero; + } + + ACCResult VisitObjCMessageExpr(ObjCMessageExpr *e) { + return checkCallToMethod(e->getMethodDecl()); + } + + ACCResult VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *e) { + ObjCMethodDecl *method; + if (e->isExplicitProperty()) + method = e->getExplicitProperty()->getGetterMethodDecl(); + else + method = e->getImplicitPropertyGetter(); + return checkCallToMethod(method); + } + + ACCResult checkCallToMethod(ObjCMethodDecl *method) { + if (!method) return ACC_invalid; + + // Check for message sends to functions returning CF types. We + // just obey the Cocoa conventions with these, even though the + // return type is CF. + if (!isAnyRetainable(TargetClass) || !isCFType(method->getReturnType())) + return ACC_invalid; + + // If the method is explicitly marked not-retained, it's +0. + if (method->hasAttr<CFReturnsNotRetainedAttr>()) + return ACC_plusZero; + + // If the method is explicitly marked as returning retained, or its + // selector follows a +1 Cocoa convention, treat it as +1. + if (method->hasAttr<CFReturnsRetainedAttr>()) + return ACC_plusOne; + + switch (method->getSelector().getMethodFamily()) { + case OMF_alloc: + case OMF_copy: + case OMF_mutableCopy: + case OMF_new: + return ACC_plusOne; + + default: + // Otherwise, treat it as +0. + return ACC_plusZero; + } + } + }; +} // end anonymous namespace + +bool Sema::isKnownName(StringRef name) { + if (name.empty()) + return false; + LookupResult R(*this, &Context.Idents.get(name), SourceLocation(), + Sema::LookupOrdinaryName); + return LookupName(R, TUScope, false); +} + +template <typename DiagBuilderT> +static void addFixitForObjCARCConversion( + Sema &S, DiagBuilderT &DiagB, Sema::CheckedConversionKind CCK, + SourceLocation afterLParen, QualType castType, Expr *castExpr, + Expr *realCast, const char *bridgeKeyword, const char *CFBridgeName) { + // We handle C-style and implicit casts here. + switch (CCK) { + case Sema::CCK_ImplicitConversion: + case Sema::CCK_ForBuiltinOverloadedOp: + case Sema::CCK_CStyleCast: + case Sema::CCK_OtherCast: + break; + case Sema::CCK_FunctionalCast: + return; + } + + if (CFBridgeName) { + if (CCK == Sema::CCK_OtherCast) { + if (const CXXNamedCastExpr *NCE = dyn_cast<CXXNamedCastExpr>(realCast)) { + SourceRange range(NCE->getOperatorLoc(), + NCE->getAngleBrackets().getEnd()); + SmallString<32> BridgeCall; + + SourceManager &SM = S.getSourceManager(); + char PrevChar = *SM.getCharacterData(range.getBegin().getLocWithOffset(-1)); + if (Lexer::isAsciiIdentifierContinueChar(PrevChar, S.getLangOpts())) + BridgeCall += ' '; + + BridgeCall += CFBridgeName; + DiagB.AddFixItHint(FixItHint::CreateReplacement(range, BridgeCall)); + } + return; + } + Expr *castedE = castExpr; + if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(castedE)) + castedE = CCE->getSubExpr(); + castedE = castedE->IgnoreImpCasts(); + SourceRange range = castedE->getSourceRange(); + + SmallString<32> BridgeCall; + + SourceManager &SM = S.getSourceManager(); + char PrevChar = *SM.getCharacterData(range.getBegin().getLocWithOffset(-1)); + if (Lexer::isAsciiIdentifierContinueChar(PrevChar, S.getLangOpts())) + BridgeCall += ' '; + + BridgeCall += CFBridgeName; + + if (isa<ParenExpr>(castedE)) { + DiagB.AddFixItHint(FixItHint::CreateInsertion(range.getBegin(), + BridgeCall)); + } else { + BridgeCall += '('; + DiagB.AddFixItHint(FixItHint::CreateInsertion(range.getBegin(), + BridgeCall)); + DiagB.AddFixItHint(FixItHint::CreateInsertion( + S.getLocForEndOfToken(range.getEnd()), + ")")); + } + return; + } + + if (CCK == Sema::CCK_CStyleCast) { + DiagB.AddFixItHint(FixItHint::CreateInsertion(afterLParen, bridgeKeyword)); + } else if (CCK == Sema::CCK_OtherCast) { + if (const CXXNamedCastExpr *NCE = dyn_cast<CXXNamedCastExpr>(realCast)) { + std::string castCode = "("; + castCode += bridgeKeyword; + castCode += castType.getAsString(); + castCode += ")"; + SourceRange Range(NCE->getOperatorLoc(), + NCE->getAngleBrackets().getEnd()); + DiagB.AddFixItHint(FixItHint::CreateReplacement(Range, castCode)); + } + } else { + std::string castCode = "("; + castCode += bridgeKeyword; + castCode += castType.getAsString(); + castCode += ")"; + Expr *castedE = castExpr->IgnoreImpCasts(); + SourceRange range = castedE->getSourceRange(); + if (isa<ParenExpr>(castedE)) { + DiagB.AddFixItHint(FixItHint::CreateInsertion(range.getBegin(), + castCode)); + } else { + castCode += "("; + DiagB.AddFixItHint(FixItHint::CreateInsertion(range.getBegin(), + castCode)); + DiagB.AddFixItHint(FixItHint::CreateInsertion( + S.getLocForEndOfToken(range.getEnd()), + ")")); + } + } +} + +template <typename T> +static inline T *getObjCBridgeAttr(const TypedefType *TD) { + TypedefNameDecl *TDNDecl = TD->getDecl(); + QualType QT = TDNDecl->getUnderlyingType(); + if (QT->isPointerType()) { + QT = QT->getPointeeType(); + if (const RecordType *RT = QT->getAs<RecordType>()) { + for (auto *Redecl : RT->getDecl()->getMostRecentDecl()->redecls()) { + if (auto *attr = Redecl->getAttr<T>()) + return attr; + } + } + } + return nullptr; +} + +static ObjCBridgeRelatedAttr *ObjCBridgeRelatedAttrFromType(QualType T, + TypedefNameDecl *&TDNDecl) { + while (const TypedefType *TD = dyn_cast<TypedefType>(T.getTypePtr())) { + TDNDecl = TD->getDecl(); + if (ObjCBridgeRelatedAttr *ObjCBAttr = + getObjCBridgeAttr<ObjCBridgeRelatedAttr>(TD)) + return ObjCBAttr; + T = TDNDecl->getUnderlyingType(); + } + return nullptr; +} + +static void +diagnoseObjCARCConversion(Sema &S, SourceRange castRange, + QualType castType, ARCConversionTypeClass castACTC, + Expr *castExpr, Expr *realCast, + ARCConversionTypeClass exprACTC, + Sema::CheckedConversionKind CCK) { + SourceLocation loc = + (castRange.isValid() ? castRange.getBegin() : castExpr->getExprLoc()); + + if (S.makeUnavailableInSystemHeader(loc, + UnavailableAttr::IR_ARCForbiddenConversion)) + return; + + QualType castExprType = castExpr->getType(); + // Defer emitting a diagnostic for bridge-related casts; that will be + // handled by CheckObjCBridgeRelatedConversions. + TypedefNameDecl *TDNDecl = nullptr; + if ((castACTC == ACTC_coreFoundation && exprACTC == ACTC_retainable && + ObjCBridgeRelatedAttrFromType(castType, TDNDecl)) || + (exprACTC == ACTC_coreFoundation && castACTC == ACTC_retainable && + ObjCBridgeRelatedAttrFromType(castExprType, TDNDecl))) + return; + + unsigned srcKind = 0; + switch (exprACTC) { + case ACTC_none: + case ACTC_coreFoundation: + case ACTC_voidPtr: + srcKind = (castExprType->isPointerType() ? 1 : 0); + break; + case ACTC_retainable: + srcKind = (castExprType->isBlockPointerType() ? 2 : 3); + break; + case ACTC_indirectRetainable: + srcKind = 4; + break; + } + + // Check whether this could be fixed with a bridge cast. + SourceLocation afterLParen = S.getLocForEndOfToken(castRange.getBegin()); + SourceLocation noteLoc = afterLParen.isValid() ? afterLParen : loc; + + unsigned convKindForDiag = Sema::isCast(CCK) ? 0 : 1; + + // Bridge from an ARC type to a CF type. + if (castACTC == ACTC_retainable && isAnyRetainable(exprACTC)) { + + S.Diag(loc, diag::err_arc_cast_requires_bridge) + << convKindForDiag + << 2 // of C pointer type + << castExprType + << unsigned(castType->isBlockPointerType()) // to ObjC|block type + << castType + << castRange + << castExpr->getSourceRange(); + bool br = S.isKnownName("CFBridgingRelease"); + ACCResult CreateRule = + ARCCastChecker(S.Context, exprACTC, castACTC, true).Visit(castExpr); + assert(CreateRule != ACC_bottom && "This cast should already be accepted."); + if (CreateRule != ACC_plusOne) + { + auto DiagB = (CCK != Sema::CCK_OtherCast) + ? S.Diag(noteLoc, diag::note_arc_bridge) + : S.Diag(noteLoc, diag::note_arc_cstyle_bridge); + + addFixitForObjCARCConversion(S, DiagB, CCK, afterLParen, + castType, castExpr, realCast, "__bridge ", + nullptr); + } + if (CreateRule != ACC_plusZero) + { + auto DiagB = (CCK == Sema::CCK_OtherCast && !br) + ? S.Diag(noteLoc, diag::note_arc_cstyle_bridge_transfer) + << castExprType + : S.Diag(br ? castExpr->getExprLoc() : noteLoc, + diag::note_arc_bridge_transfer) + << castExprType << br; + + addFixitForObjCARCConversion(S, DiagB, CCK, afterLParen, + castType, castExpr, realCast, "__bridge_transfer ", + br ? "CFBridgingRelease" : nullptr); + } + + return; + } + + // Bridge from a CF type to an ARC type. + if (exprACTC == ACTC_retainable && isAnyRetainable(castACTC)) { + bool br = S.isKnownName("CFBridgingRetain"); + S.Diag(loc, diag::err_arc_cast_requires_bridge) + << convKindForDiag + << unsigned(castExprType->isBlockPointerType()) // of ObjC|block type + << castExprType + << 2 // to C pointer type + << castType + << castRange + << castExpr->getSourceRange(); + ACCResult CreateRule = + ARCCastChecker(S.Context, exprACTC, castACTC, true).Visit(castExpr); + assert(CreateRule != ACC_bottom && "This cast should already be accepted."); + if (CreateRule != ACC_plusOne) + { + auto DiagB = (CCK != Sema::CCK_OtherCast) + ? S.Diag(noteLoc, diag::note_arc_bridge) + : S.Diag(noteLoc, diag::note_arc_cstyle_bridge); + addFixitForObjCARCConversion(S, DiagB, CCK, afterLParen, + castType, castExpr, realCast, "__bridge ", + nullptr); + } + if (CreateRule != ACC_plusZero) + { + auto DiagB = (CCK == Sema::CCK_OtherCast && !br) + ? S.Diag(noteLoc, diag::note_arc_cstyle_bridge_retained) + << castType + : S.Diag(br ? castExpr->getExprLoc() : noteLoc, + diag::note_arc_bridge_retained) + << castType << br; + + addFixitForObjCARCConversion(S, DiagB, CCK, afterLParen, + castType, castExpr, realCast, "__bridge_retained ", + br ? "CFBridgingRetain" : nullptr); + } + + return; + } + + S.Diag(loc, diag::err_arc_mismatched_cast) + << !convKindForDiag + << srcKind << castExprType << castType + << castRange << castExpr->getSourceRange(); +} + +template <typename TB> +static bool CheckObjCBridgeNSCast(Sema &S, QualType castType, Expr *castExpr, + bool &HadTheAttribute, bool warn) { + QualType T = castExpr->getType(); + HadTheAttribute = false; + while (const TypedefType *TD = dyn_cast<TypedefType>(T.getTypePtr())) { + TypedefNameDecl *TDNDecl = TD->getDecl(); + if (TB *ObjCBAttr = getObjCBridgeAttr<TB>(TD)) { + if (IdentifierInfo *Parm = ObjCBAttr->getBridgedType()) { + HadTheAttribute = true; + if (Parm->isStr("id")) + return true; + + // Check for an existing type with this name. + LookupResult R(S, DeclarationName(Parm), SourceLocation(), + Sema::LookupOrdinaryName); + if (S.LookupName(R, S.TUScope)) { + NamedDecl *Target = R.getFoundDecl(); + if (Target && isa<ObjCInterfaceDecl>(Target)) { + ObjCInterfaceDecl *ExprClass = cast<ObjCInterfaceDecl>(Target); + if (const ObjCObjectPointerType *InterfacePointerType = + castType->getAsObjCInterfacePointerType()) { + ObjCInterfaceDecl *CastClass + = InterfacePointerType->getObjectType()->getInterface(); + if ((CastClass == ExprClass) || + (CastClass && CastClass->isSuperClassOf(ExprClass))) + return true; + if (warn) + S.Diag(castExpr->getBeginLoc(), diag::warn_objc_invalid_bridge) + << T << Target->getName() << castType->getPointeeType(); + return false; + } else if (castType->isObjCIdType() || + (S.Context.ObjCObjectAdoptsQTypeProtocols( + castType, ExprClass))) + // ok to cast to 'id'. + // casting to id<p-list> is ok if bridge type adopts all of + // p-list protocols. + return true; + else { + if (warn) { + S.Diag(castExpr->getBeginLoc(), diag::warn_objc_invalid_bridge) + << T << Target->getName() << castType; + S.Diag(TDNDecl->getBeginLoc(), diag::note_declared_at); + S.Diag(Target->getBeginLoc(), diag::note_declared_at); + } + return false; + } + } + } else if (!castType->isObjCIdType()) { + S.Diag(castExpr->getBeginLoc(), + diag::err_objc_cf_bridged_not_interface) + << castExpr->getType() << Parm; + S.Diag(TDNDecl->getBeginLoc(), diag::note_declared_at); + } + return true; + } + return false; + } + T = TDNDecl->getUnderlyingType(); + } + return true; +} + +template <typename TB> +static bool CheckObjCBridgeCFCast(Sema &S, QualType castType, Expr *castExpr, + bool &HadTheAttribute, bool warn) { + QualType T = castType; + HadTheAttribute = false; + while (const TypedefType *TD = dyn_cast<TypedefType>(T.getTypePtr())) { + TypedefNameDecl *TDNDecl = TD->getDecl(); + if (TB *ObjCBAttr = getObjCBridgeAttr<TB>(TD)) { + if (IdentifierInfo *Parm = ObjCBAttr->getBridgedType()) { + HadTheAttribute = true; + if (Parm->isStr("id")) + return true; + + NamedDecl *Target = nullptr; + // Check for an existing type with this name. + LookupResult R(S, DeclarationName(Parm), SourceLocation(), + Sema::LookupOrdinaryName); + if (S.LookupName(R, S.TUScope)) { + Target = R.getFoundDecl(); + if (Target && isa<ObjCInterfaceDecl>(Target)) { + ObjCInterfaceDecl *CastClass = cast<ObjCInterfaceDecl>(Target); + if (const ObjCObjectPointerType *InterfacePointerType = + castExpr->getType()->getAsObjCInterfacePointerType()) { + ObjCInterfaceDecl *ExprClass + = InterfacePointerType->getObjectType()->getInterface(); + if ((CastClass == ExprClass) || + (ExprClass && CastClass->isSuperClassOf(ExprClass))) + return true; + if (warn) { + S.Diag(castExpr->getBeginLoc(), + diag::warn_objc_invalid_bridge_to_cf) + << castExpr->getType()->getPointeeType() << T; + S.Diag(TDNDecl->getBeginLoc(), diag::note_declared_at); + } + return false; + } else if (castExpr->getType()->isObjCIdType() || + (S.Context.QIdProtocolsAdoptObjCObjectProtocols( + castExpr->getType(), CastClass))) + // ok to cast an 'id' expression to a CFtype. + // ok to cast an 'id<plist>' expression to CFtype provided plist + // adopts all of CFtype's ObjetiveC's class plist. + return true; + else { + if (warn) { + S.Diag(castExpr->getBeginLoc(), + diag::warn_objc_invalid_bridge_to_cf) + << castExpr->getType() << castType; + S.Diag(TDNDecl->getBeginLoc(), diag::note_declared_at); + S.Diag(Target->getBeginLoc(), diag::note_declared_at); + } + return false; + } + } + } + S.Diag(castExpr->getBeginLoc(), + diag::err_objc_ns_bridged_invalid_cfobject) + << castExpr->getType() << castType; + S.Diag(TDNDecl->getBeginLoc(), diag::note_declared_at); + if (Target) + S.Diag(Target->getBeginLoc(), diag::note_declared_at); + return true; + } + return false; + } + T = TDNDecl->getUnderlyingType(); + } + return true; +} + +void Sema::CheckTollFreeBridgeCast(QualType castType, Expr *castExpr) { + if (!getLangOpts().ObjC) + return; + // warn in presence of __bridge casting to or from a toll free bridge cast. + ARCConversionTypeClass exprACTC = classifyTypeForARCConversion(castExpr->getType()); + ARCConversionTypeClass castACTC = classifyTypeForARCConversion(castType); + if (castACTC == ACTC_retainable && exprACTC == ACTC_coreFoundation) { + bool HasObjCBridgeAttr; + bool ObjCBridgeAttrWillNotWarn = + CheckObjCBridgeNSCast<ObjCBridgeAttr>(*this, castType, castExpr, HasObjCBridgeAttr, + false); + if (ObjCBridgeAttrWillNotWarn && HasObjCBridgeAttr) + return; + bool HasObjCBridgeMutableAttr; + bool ObjCBridgeMutableAttrWillNotWarn = + CheckObjCBridgeNSCast<ObjCBridgeMutableAttr>(*this, castType, castExpr, + HasObjCBridgeMutableAttr, false); + if (ObjCBridgeMutableAttrWillNotWarn && HasObjCBridgeMutableAttr) + return; + + if (HasObjCBridgeAttr) + CheckObjCBridgeNSCast<ObjCBridgeAttr>(*this, castType, castExpr, HasObjCBridgeAttr, + true); + else if (HasObjCBridgeMutableAttr) + CheckObjCBridgeNSCast<ObjCBridgeMutableAttr>(*this, castType, castExpr, + HasObjCBridgeMutableAttr, true); + } + else if (castACTC == ACTC_coreFoundation && exprACTC == ACTC_retainable) { + bool HasObjCBridgeAttr; + bool ObjCBridgeAttrWillNotWarn = + CheckObjCBridgeCFCast<ObjCBridgeAttr>(*this, castType, castExpr, HasObjCBridgeAttr, + false); + if (ObjCBridgeAttrWillNotWarn && HasObjCBridgeAttr) + return; + bool HasObjCBridgeMutableAttr; + bool ObjCBridgeMutableAttrWillNotWarn = + CheckObjCBridgeCFCast<ObjCBridgeMutableAttr>(*this, castType, castExpr, + HasObjCBridgeMutableAttr, false); + if (ObjCBridgeMutableAttrWillNotWarn && HasObjCBridgeMutableAttr) + return; + + if (HasObjCBridgeAttr) + CheckObjCBridgeCFCast<ObjCBridgeAttr>(*this, castType, castExpr, HasObjCBridgeAttr, + true); + else if (HasObjCBridgeMutableAttr) + CheckObjCBridgeCFCast<ObjCBridgeMutableAttr>(*this, castType, castExpr, + HasObjCBridgeMutableAttr, true); + } +} + +void Sema::CheckObjCBridgeRelatedCast(QualType castType, Expr *castExpr) { + QualType SrcType = castExpr->getType(); + if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(castExpr)) { + if (PRE->isExplicitProperty()) { + if (ObjCPropertyDecl *PDecl = PRE->getExplicitProperty()) + SrcType = PDecl->getType(); + } + else if (PRE->isImplicitProperty()) { + if (ObjCMethodDecl *Getter = PRE->getImplicitPropertyGetter()) + SrcType = Getter->getReturnType(); + } + } + + ARCConversionTypeClass srcExprACTC = classifyTypeForARCConversion(SrcType); + ARCConversionTypeClass castExprACTC = classifyTypeForARCConversion(castType); + if (srcExprACTC != ACTC_retainable || castExprACTC != ACTC_coreFoundation) + return; + CheckObjCBridgeRelatedConversions(castExpr->getBeginLoc(), castType, SrcType, + castExpr); +} + +bool Sema::CheckTollFreeBridgeStaticCast(QualType castType, Expr *castExpr, + CastKind &Kind) { + if (!getLangOpts().ObjC) + return false; + ARCConversionTypeClass exprACTC = + classifyTypeForARCConversion(castExpr->getType()); + ARCConversionTypeClass castACTC = classifyTypeForARCConversion(castType); + if ((castACTC == ACTC_retainable && exprACTC == ACTC_coreFoundation) || + (castACTC == ACTC_coreFoundation && exprACTC == ACTC_retainable)) { + CheckTollFreeBridgeCast(castType, castExpr); + Kind = (castACTC == ACTC_coreFoundation) ? CK_BitCast + : CK_CPointerToObjCPointerCast; + return true; + } + return false; +} + +bool Sema::checkObjCBridgeRelatedComponents(SourceLocation Loc, + QualType DestType, QualType SrcType, + ObjCInterfaceDecl *&RelatedClass, + ObjCMethodDecl *&ClassMethod, + ObjCMethodDecl *&InstanceMethod, + TypedefNameDecl *&TDNDecl, + bool CfToNs, bool Diagnose) { + QualType T = CfToNs ? SrcType : DestType; + ObjCBridgeRelatedAttr *ObjCBAttr = ObjCBridgeRelatedAttrFromType(T, TDNDecl); + if (!ObjCBAttr) + return false; + + IdentifierInfo *RCId = ObjCBAttr->getRelatedClass(); + IdentifierInfo *CMId = ObjCBAttr->getClassMethod(); + IdentifierInfo *IMId = ObjCBAttr->getInstanceMethod(); + if (!RCId) + return false; + NamedDecl *Target = nullptr; + // Check for an existing type with this name. + LookupResult R(*this, DeclarationName(RCId), SourceLocation(), + Sema::LookupOrdinaryName); + if (!LookupName(R, TUScope)) { + if (Diagnose) { + Diag(Loc, diag::err_objc_bridged_related_invalid_class) << RCId + << SrcType << DestType; + Diag(TDNDecl->getBeginLoc(), diag::note_declared_at); + } + return false; + } + Target = R.getFoundDecl(); + if (Target && isa<ObjCInterfaceDecl>(Target)) + RelatedClass = cast<ObjCInterfaceDecl>(Target); + else { + if (Diagnose) { + Diag(Loc, diag::err_objc_bridged_related_invalid_class_name) << RCId + << SrcType << DestType; + Diag(TDNDecl->getBeginLoc(), diag::note_declared_at); + if (Target) + Diag(Target->getBeginLoc(), diag::note_declared_at); + } + return false; + } + + // Check for an existing class method with the given selector name. + if (CfToNs && CMId) { + Selector Sel = Context.Selectors.getUnarySelector(CMId); + ClassMethod = RelatedClass->lookupMethod(Sel, false); + if (!ClassMethod) { + if (Diagnose) { + Diag(Loc, diag::err_objc_bridged_related_known_method) + << SrcType << DestType << Sel << false; + Diag(TDNDecl->getBeginLoc(), diag::note_declared_at); + } + return false; + } + } + + // Check for an existing instance method with the given selector name. + if (!CfToNs && IMId) { + Selector Sel = Context.Selectors.getNullarySelector(IMId); + InstanceMethod = RelatedClass->lookupMethod(Sel, true); + if (!InstanceMethod) { + if (Diagnose) { + Diag(Loc, diag::err_objc_bridged_related_known_method) + << SrcType << DestType << Sel << true; + Diag(TDNDecl->getBeginLoc(), diag::note_declared_at); + } + return false; + } + } + return true; +} + +bool +Sema::CheckObjCBridgeRelatedConversions(SourceLocation Loc, + QualType DestType, QualType SrcType, + Expr *&SrcExpr, bool Diagnose) { + ARCConversionTypeClass rhsExprACTC = classifyTypeForARCConversion(SrcType); + ARCConversionTypeClass lhsExprACTC = classifyTypeForARCConversion(DestType); + bool CfToNs = (rhsExprACTC == ACTC_coreFoundation && lhsExprACTC == ACTC_retainable); + bool NsToCf = (rhsExprACTC == ACTC_retainable && lhsExprACTC == ACTC_coreFoundation); + if (!CfToNs && !NsToCf) + return false; + + ObjCInterfaceDecl *RelatedClass; + ObjCMethodDecl *ClassMethod = nullptr; + ObjCMethodDecl *InstanceMethod = nullptr; + TypedefNameDecl *TDNDecl = nullptr; + if (!checkObjCBridgeRelatedComponents(Loc, DestType, SrcType, RelatedClass, + ClassMethod, InstanceMethod, TDNDecl, + CfToNs, Diagnose)) + return false; + + if (CfToNs) { + // Implicit conversion from CF to ObjC object is needed. + if (ClassMethod) { + if (Diagnose) { + std::string ExpressionString = "["; + ExpressionString += RelatedClass->getNameAsString(); + ExpressionString += " "; + ExpressionString += ClassMethod->getSelector().getAsString(); + SourceLocation SrcExprEndLoc = + getLocForEndOfToken(SrcExpr->getEndLoc()); + // Provide a fixit: [RelatedClass ClassMethod SrcExpr] + Diag(Loc, diag::err_objc_bridged_related_known_method) + << SrcType << DestType << ClassMethod->getSelector() << false + << FixItHint::CreateInsertion(SrcExpr->getBeginLoc(), + ExpressionString) + << FixItHint::CreateInsertion(SrcExprEndLoc, "]"); + Diag(RelatedClass->getBeginLoc(), diag::note_declared_at); + Diag(TDNDecl->getBeginLoc(), diag::note_declared_at); + + QualType receiverType = Context.getObjCInterfaceType(RelatedClass); + // Argument. + Expr *args[] = { SrcExpr }; + ExprResult msg = BuildClassMessageImplicit(receiverType, false, + ClassMethod->getLocation(), + ClassMethod->getSelector(), ClassMethod, + MultiExprArg(args, 1)); + SrcExpr = msg.get(); + } + return true; + } + } + else { + // Implicit conversion from ObjC type to CF object is needed. + if (InstanceMethod) { + if (Diagnose) { + std::string ExpressionString; + SourceLocation SrcExprEndLoc = + getLocForEndOfToken(SrcExpr->getEndLoc()); + if (InstanceMethod->isPropertyAccessor()) + if (const ObjCPropertyDecl *PDecl = + InstanceMethod->findPropertyDecl()) { + // fixit: ObjectExpr.propertyname when it is aproperty accessor. + ExpressionString = "."; + ExpressionString += PDecl->getNameAsString(); + Diag(Loc, diag::err_objc_bridged_related_known_method) + << SrcType << DestType << InstanceMethod->getSelector() << true + << FixItHint::CreateInsertion(SrcExprEndLoc, ExpressionString); + } + if (ExpressionString.empty()) { + // Provide a fixit: [ObjectExpr InstanceMethod] + ExpressionString = " "; + ExpressionString += InstanceMethod->getSelector().getAsString(); + ExpressionString += "]"; + + Diag(Loc, diag::err_objc_bridged_related_known_method) + << SrcType << DestType << InstanceMethod->getSelector() << true + << FixItHint::CreateInsertion(SrcExpr->getBeginLoc(), "[") + << FixItHint::CreateInsertion(SrcExprEndLoc, ExpressionString); + } + Diag(RelatedClass->getBeginLoc(), diag::note_declared_at); + Diag(TDNDecl->getBeginLoc(), diag::note_declared_at); + + ExprResult msg = + BuildInstanceMessageImplicit(SrcExpr, SrcType, + InstanceMethod->getLocation(), + InstanceMethod->getSelector(), + InstanceMethod, None); + SrcExpr = msg.get(); + } + return true; + } + } + return false; +} + +Sema::ARCConversionResult +Sema::CheckObjCConversion(SourceRange castRange, QualType castType, + Expr *&castExpr, CheckedConversionKind CCK, + bool Diagnose, bool DiagnoseCFAudited, + BinaryOperatorKind Opc) { + QualType castExprType = castExpr->getType(); + + // For the purposes of the classification, we assume reference types + // will bind to temporaries. + QualType effCastType = castType; + if (const ReferenceType *ref = castType->getAs<ReferenceType>()) + effCastType = ref->getPointeeType(); + + ARCConversionTypeClass exprACTC = classifyTypeForARCConversion(castExprType); + ARCConversionTypeClass castACTC = classifyTypeForARCConversion(effCastType); + if (exprACTC == castACTC) { + // Check for viability and report error if casting an rvalue to a + // life-time qualifier. + if (castACTC == ACTC_retainable && + (CCK == CCK_CStyleCast || CCK == CCK_OtherCast) && + castType != castExprType) { + const Type *DT = castType.getTypePtr(); + QualType QDT = castType; + // We desugar some types but not others. We ignore those + // that cannot happen in a cast; i.e. auto, and those which + // should not be de-sugared; i.e typedef. + if (const ParenType *PT = dyn_cast<ParenType>(DT)) + QDT = PT->desugar(); + else if (const TypeOfType *TP = dyn_cast<TypeOfType>(DT)) + QDT = TP->desugar(); + else if (const AttributedType *AT = dyn_cast<AttributedType>(DT)) + QDT = AT->desugar(); + if (QDT != castType && + QDT.getObjCLifetime() != Qualifiers::OCL_None) { + if (Diagnose) { + SourceLocation loc = (castRange.isValid() ? castRange.getBegin() + : castExpr->getExprLoc()); + Diag(loc, diag::err_arc_nolifetime_behavior); + } + return ACR_error; + } + } + return ACR_okay; + } + + // The life-time qualifier cast check above is all we need for ObjCWeak. + // ObjCAutoRefCount has more restrictions on what is legal. + if (!getLangOpts().ObjCAutoRefCount) + return ACR_okay; + + if (isAnyCLike(exprACTC) && isAnyCLike(castACTC)) return ACR_okay; + + // Allow all of these types to be cast to integer types (but not + // vice-versa). + if (castACTC == ACTC_none && castType->isIntegralType(Context)) + return ACR_okay; + + // Allow casts between pointers to lifetime types (e.g., __strong id*) + // and pointers to void (e.g., cv void *). Casting from void* to lifetime* + // must be explicit. + // Allow conversions between pointers to lifetime types and coreFoundation + // pointers too, but only when the conversions are explicit. + if (exprACTC == ACTC_indirectRetainable && + (castACTC == ACTC_voidPtr || + (castACTC == ACTC_coreFoundation && isCast(CCK)))) + return ACR_okay; + if (castACTC == ACTC_indirectRetainable && + (exprACTC == ACTC_voidPtr || exprACTC == ACTC_coreFoundation) && + isCast(CCK)) + return ACR_okay; + + switch (ARCCastChecker(Context, exprACTC, castACTC, false).Visit(castExpr)) { + // For invalid casts, fall through. + case ACC_invalid: + break; + + // Do nothing for both bottom and +0. + case ACC_bottom: + case ACC_plusZero: + return ACR_okay; + + // If the result is +1, consume it here. + case ACC_plusOne: + castExpr = ImplicitCastExpr::Create(Context, castExpr->getType(), + CK_ARCConsumeObject, castExpr, nullptr, + VK_PRValue, FPOptionsOverride()); + Cleanup.setExprNeedsCleanups(true); + return ACR_okay; + } + + // If this is a non-implicit cast from id or block type to a + // CoreFoundation type, delay complaining in case the cast is used + // in an acceptable context. + if (exprACTC == ACTC_retainable && isAnyRetainable(castACTC) && isCast(CCK)) + return ACR_unbridged; + + // Issue a diagnostic about a missing @-sign when implicit casting a cstring + // to 'NSString *', instead of falling through to report a "bridge cast" + // diagnostic. + if (castACTC == ACTC_retainable && exprACTC == ACTC_none && + CheckConversionToObjCLiteral(castType, castExpr, Diagnose)) + return ACR_error; + + // Do not issue "bridge cast" diagnostic when implicit casting + // a retainable object to a CF type parameter belonging to an audited + // CF API function. Let caller issue a normal type mismatched diagnostic + // instead. + if ((!DiagnoseCFAudited || exprACTC != ACTC_retainable || + castACTC != ACTC_coreFoundation) && + !(exprACTC == ACTC_voidPtr && castACTC == ACTC_retainable && + (Opc == BO_NE || Opc == BO_EQ))) { + if (Diagnose) + diagnoseObjCARCConversion(*this, castRange, castType, castACTC, castExpr, + castExpr, exprACTC, CCK); + return ACR_error; + } + return ACR_okay; +} + +/// Given that we saw an expression with the ARCUnbridgedCastTy +/// placeholder type, complain bitterly. +void Sema::diagnoseARCUnbridgedCast(Expr *e) { + // We expect the spurious ImplicitCastExpr to already have been stripped. + assert(!e->hasPlaceholderType(BuiltinType::ARCUnbridgedCast)); + CastExpr *realCast = cast<CastExpr>(e->IgnoreParens()); + + SourceRange castRange; + QualType castType; + CheckedConversionKind CCK; + + if (CStyleCastExpr *cast = dyn_cast<CStyleCastExpr>(realCast)) { + castRange = SourceRange(cast->getLParenLoc(), cast->getRParenLoc()); + castType = cast->getTypeAsWritten(); + CCK = CCK_CStyleCast; + } else if (ExplicitCastExpr *cast = dyn_cast<ExplicitCastExpr>(realCast)) { + castRange = cast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(); + castType = cast->getTypeAsWritten(); + CCK = CCK_OtherCast; + } else { + llvm_unreachable("Unexpected ImplicitCastExpr"); + } + + ARCConversionTypeClass castACTC = + classifyTypeForARCConversion(castType.getNonReferenceType()); + + Expr *castExpr = realCast->getSubExpr(); + assert(classifyTypeForARCConversion(castExpr->getType()) == ACTC_retainable); + + diagnoseObjCARCConversion(*this, castRange, castType, castACTC, + castExpr, realCast, ACTC_retainable, CCK); +} + +/// stripARCUnbridgedCast - Given an expression of ARCUnbridgedCast +/// type, remove the placeholder cast. +Expr *Sema::stripARCUnbridgedCast(Expr *e) { + assert(e->hasPlaceholderType(BuiltinType::ARCUnbridgedCast)); + + if (ParenExpr *pe = dyn_cast<ParenExpr>(e)) { + Expr *sub = stripARCUnbridgedCast(pe->getSubExpr()); + return new (Context) ParenExpr(pe->getLParen(), pe->getRParen(), sub); + } else if (UnaryOperator *uo = dyn_cast<UnaryOperator>(e)) { + assert(uo->getOpcode() == UO_Extension); + Expr *sub = stripARCUnbridgedCast(uo->getSubExpr()); + return UnaryOperator::Create(Context, sub, UO_Extension, sub->getType(), + sub->getValueKind(), sub->getObjectKind(), + uo->getOperatorLoc(), false, + CurFPFeatureOverrides()); + } else if (GenericSelectionExpr *gse = dyn_cast<GenericSelectionExpr>(e)) { + assert(!gse->isResultDependent()); + + unsigned n = gse->getNumAssocs(); + SmallVector<Expr *, 4> subExprs; + SmallVector<TypeSourceInfo *, 4> subTypes; + subExprs.reserve(n); + subTypes.reserve(n); + for (const GenericSelectionExpr::Association assoc : gse->associations()) { + subTypes.push_back(assoc.getTypeSourceInfo()); + Expr *sub = assoc.getAssociationExpr(); + if (assoc.isSelected()) + sub = stripARCUnbridgedCast(sub); + subExprs.push_back(sub); + } + + return GenericSelectionExpr::Create( + Context, gse->getGenericLoc(), gse->getControllingExpr(), subTypes, + subExprs, gse->getDefaultLoc(), gse->getRParenLoc(), + gse->containsUnexpandedParameterPack(), gse->getResultIndex()); + } else { + assert(isa<ImplicitCastExpr>(e) && "bad form of unbridged cast!"); + return cast<ImplicitCastExpr>(e)->getSubExpr(); + } +} + +bool Sema::CheckObjCARCUnavailableWeakConversion(QualType castType, + QualType exprType) { + QualType canCastType = + Context.getCanonicalType(castType).getUnqualifiedType(); + QualType canExprType = + Context.getCanonicalType(exprType).getUnqualifiedType(); + if (isa<ObjCObjectPointerType>(canCastType) && + castType.getObjCLifetime() == Qualifiers::OCL_Weak && + canExprType->isObjCObjectPointerType()) { + if (const ObjCObjectPointerType *ObjT = + canExprType->getAs<ObjCObjectPointerType>()) + if (const ObjCInterfaceDecl *ObjI = ObjT->getInterfaceDecl()) + return !ObjI->isArcWeakrefUnavailable(); + } + return true; +} + +/// Look for an ObjCReclaimReturnedObject cast and destroy it. +static Expr *maybeUndoReclaimObject(Expr *e) { + Expr *curExpr = e, *prevExpr = nullptr; + + // Walk down the expression until we hit an implicit cast of kind + // ARCReclaimReturnedObject or an Expr that is neither a Paren nor a Cast. + while (true) { + if (auto *pe = dyn_cast<ParenExpr>(curExpr)) { + prevExpr = curExpr; + curExpr = pe->getSubExpr(); + continue; + } + + if (auto *ce = dyn_cast<CastExpr>(curExpr)) { + if (auto *ice = dyn_cast<ImplicitCastExpr>(ce)) + if (ice->getCastKind() == CK_ARCReclaimReturnedObject) { + if (!prevExpr) + return ice->getSubExpr(); + if (auto *pe = dyn_cast<ParenExpr>(prevExpr)) + pe->setSubExpr(ice->getSubExpr()); + else + cast<CastExpr>(prevExpr)->setSubExpr(ice->getSubExpr()); + return e; + } + + prevExpr = curExpr; + curExpr = ce->getSubExpr(); + continue; + } + + // Break out of the loop if curExpr is neither a Paren nor a Cast. + break; + } + + return e; +} + +ExprResult Sema::BuildObjCBridgedCast(SourceLocation LParenLoc, + ObjCBridgeCastKind Kind, + SourceLocation BridgeKeywordLoc, + TypeSourceInfo *TSInfo, + Expr *SubExpr) { + ExprResult SubResult = UsualUnaryConversions(SubExpr); + if (SubResult.isInvalid()) return ExprError(); + SubExpr = SubResult.get(); + + QualType T = TSInfo->getType(); + QualType FromType = SubExpr->getType(); + + CastKind CK; + + bool MustConsume = false; + if (T->isDependentType() || SubExpr->isTypeDependent()) { + // Okay: we'll build a dependent expression type. + CK = CK_Dependent; + } else if (T->isObjCARCBridgableType() && FromType->isCARCBridgableType()) { + // Casting CF -> id + CK = (T->isBlockPointerType() ? CK_AnyPointerToBlockPointerCast + : CK_CPointerToObjCPointerCast); + switch (Kind) { + case OBC_Bridge: + break; + + case OBC_BridgeRetained: { + bool br = isKnownName("CFBridgingRelease"); + Diag(BridgeKeywordLoc, diag::err_arc_bridge_cast_wrong_kind) + << 2 + << FromType + << (T->isBlockPointerType()? 1 : 0) + << T + << SubExpr->getSourceRange() + << Kind; + Diag(BridgeKeywordLoc, diag::note_arc_bridge) + << FixItHint::CreateReplacement(BridgeKeywordLoc, "__bridge"); + Diag(BridgeKeywordLoc, diag::note_arc_bridge_transfer) + << FromType << br + << FixItHint::CreateReplacement(BridgeKeywordLoc, + br ? "CFBridgingRelease " + : "__bridge_transfer "); + + Kind = OBC_Bridge; + break; + } + + case OBC_BridgeTransfer: + // We must consume the Objective-C object produced by the cast. + MustConsume = true; + break; + } + } else if (T->isCARCBridgableType() && FromType->isObjCARCBridgableType()) { + // Okay: id -> CF + CK = CK_BitCast; + switch (Kind) { + case OBC_Bridge: + // Reclaiming a value that's going to be __bridge-casted to CF + // is very dangerous, so we don't do it. + SubExpr = maybeUndoReclaimObject(SubExpr); + break; + + case OBC_BridgeRetained: + // Produce the object before casting it. + SubExpr = ImplicitCastExpr::Create(Context, FromType, CK_ARCProduceObject, + SubExpr, nullptr, VK_PRValue, + FPOptionsOverride()); + break; + + case OBC_BridgeTransfer: { + bool br = isKnownName("CFBridgingRetain"); + Diag(BridgeKeywordLoc, diag::err_arc_bridge_cast_wrong_kind) + << (FromType->isBlockPointerType()? 1 : 0) + << FromType + << 2 + << T + << SubExpr->getSourceRange() + << Kind; + + Diag(BridgeKeywordLoc, diag::note_arc_bridge) + << FixItHint::CreateReplacement(BridgeKeywordLoc, "__bridge "); + Diag(BridgeKeywordLoc, diag::note_arc_bridge_retained) + << T << br + << FixItHint::CreateReplacement(BridgeKeywordLoc, + br ? "CFBridgingRetain " : "__bridge_retained"); + + Kind = OBC_Bridge; + break; + } + } + } else { + Diag(LParenLoc, diag::err_arc_bridge_cast_incompatible) + << FromType << T << Kind + << SubExpr->getSourceRange() + << TSInfo->getTypeLoc().getSourceRange(); + return ExprError(); + } + + Expr *Result = new (Context) ObjCBridgedCastExpr(LParenLoc, Kind, CK, + BridgeKeywordLoc, + TSInfo, SubExpr); + + if (MustConsume) { + Cleanup.setExprNeedsCleanups(true); + Result = ImplicitCastExpr::Create(Context, T, CK_ARCConsumeObject, Result, + nullptr, VK_PRValue, FPOptionsOverride()); + } + + return Result; +} + +ExprResult Sema::ActOnObjCBridgedCast(Scope *S, + SourceLocation LParenLoc, + ObjCBridgeCastKind Kind, + SourceLocation BridgeKeywordLoc, + ParsedType Type, + SourceLocation RParenLoc, + Expr *SubExpr) { + TypeSourceInfo *TSInfo = nullptr; + QualType T = GetTypeFromParser(Type, &TSInfo); + if (Kind == OBC_Bridge) + CheckTollFreeBridgeCast(T, SubExpr); + if (!TSInfo) + TSInfo = Context.getTrivialTypeSourceInfo(T, LParenLoc); + return BuildObjCBridgedCast(LParenLoc, Kind, BridgeKeywordLoc, TSInfo, + SubExpr); +} |