//===----- SemaObjC.cpp ---- Semantic Analysis for Objective-C ------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// This file implements semantic analysis for Objective-C.
///
//===----------------------------------------------------------------------===//

#include "clang/Sema/SemaObjC.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/AST/StmtObjC.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Attr.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/TemplateDeduction.h"
#include "llvm/Support/ConvertUTF.h"

namespace clang {

SemaObjC::SemaObjC(Sema &S)
    : SemaBase(S), NSNumberDecl(nullptr), NSValueDecl(nullptr),
      NSStringDecl(nullptr), StringWithUTF8StringMethod(nullptr),
      ValueWithBytesObjCTypeMethod(nullptr), NSArrayDecl(nullptr),
      ArrayWithObjectsMethod(nullptr), NSDictionaryDecl(nullptr),
      DictionaryWithObjectsMethod(nullptr) {}

StmtResult SemaObjC::ActOnObjCForCollectionStmt(SourceLocation ForLoc,
                                                Stmt *First, Expr *collection,
                                                SourceLocation RParenLoc) {
  ASTContext &Context = getASTContext();
  SemaRef.setFunctionHasBranchProtectedScope();

  ExprResult CollectionExprResult =
      CheckObjCForCollectionOperand(ForLoc, collection);

  if (First) {
    QualType FirstType;
    if (DeclStmt *DS = dyn_cast<DeclStmt>(First)) {
      if (!DS->isSingleDecl())
        return StmtError(Diag((*DS->decl_begin())->getLocation(),
                              diag::err_toomany_element_decls));

      VarDecl *D = dyn_cast<VarDecl>(DS->getSingleDecl());
      if (!D || D->isInvalidDecl())
        return StmtError();

      FirstType = D->getType();
      // C99 6.8.5p3: The declaration part of a 'for' statement shall only
      // declare identifiers for objects having storage class 'auto' or
      // 'register'.
      if (!D->hasLocalStorage())
        return StmtError(
            Diag(D->getLocation(), diag::err_non_local_variable_decl_in_for));

      // If the type contained 'auto', deduce the 'auto' to 'id'.
      if (FirstType->getContainedAutoType()) {
        SourceLocation Loc = D->getLocation();
        OpaqueValueExpr OpaqueId(Loc, Context.getObjCIdType(), VK_PRValue);
        Expr *DeducedInit = &OpaqueId;
        sema::TemplateDeductionInfo Info(Loc);
        FirstType = QualType();
        TemplateDeductionResult Result = SemaRef.DeduceAutoType(
            D->getTypeSourceInfo()->getTypeLoc(), DeducedInit, FirstType, Info);
        if (Result != TemplateDeductionResult::Success &&
            Result != TemplateDeductionResult::AlreadyDiagnosed)
          SemaRef.DiagnoseAutoDeductionFailure(D, DeducedInit);
        if (FirstType.isNull()) {
          D->setInvalidDecl();
          return StmtError();
        }

        D->setType(FirstType);

        if (!SemaRef.inTemplateInstantiation()) {
          SourceLocation Loc =
              D->getTypeSourceInfo()->getTypeLoc().getBeginLoc();
          Diag(Loc, diag::warn_auto_var_is_id) << D->getDeclName();
        }
      }

    } else {
      Expr *FirstE = cast<Expr>(First);
      if (!FirstE->isTypeDependent() && !FirstE->isLValue())
        return StmtError(
            Diag(First->getBeginLoc(), diag::err_selector_element_not_lvalue)
            << First->getSourceRange());

      FirstType = static_cast<Expr *>(First)->getType();
      if (FirstType.isConstQualified())
        Diag(ForLoc, diag::err_selector_element_const_type)
            << FirstType << First->getSourceRange();
    }
    if (!FirstType->isDependentType() &&
        !FirstType->isObjCObjectPointerType() &&
        !FirstType->isBlockPointerType())
      return StmtError(Diag(ForLoc, diag::err_selector_element_type)
                       << FirstType << First->getSourceRange());
  }

  if (CollectionExprResult.isInvalid())
    return StmtError();

  CollectionExprResult = SemaRef.ActOnFinishFullExpr(CollectionExprResult.get(),
                                                     /*DiscardedValue*/ false);
  if (CollectionExprResult.isInvalid())
    return StmtError();

  return new (Context) ObjCForCollectionStmt(First, CollectionExprResult.get(),
                                             nullptr, ForLoc, RParenLoc);
}

ExprResult SemaObjC::CheckObjCForCollectionOperand(SourceLocation forLoc,
                                                   Expr *collection) {
  ASTContext &Context = getASTContext();
  if (!collection)
    return ExprError();

  // Bail out early if we've got a type-dependent expression.
  if (collection->isTypeDependent())
    return collection;

  // Perform normal l-value conversion.
  ExprResult result = SemaRef.DefaultFunctionArrayLvalueConversion(collection);
  if (result.isInvalid())
    return ExprError();
  collection = result.get();

  // The operand needs to have object-pointer type.
  // TODO: should we do a contextual conversion?
  const ObjCObjectPointerType *pointerType =
      collection->getType()->getAs<ObjCObjectPointerType>();
  if (!pointerType)
    return Diag(forLoc, diag::err_collection_expr_type)
           << collection->getType() << collection->getSourceRange();

  // Check that the operand provides
  //   - countByEnumeratingWithState:objects:count:
  const ObjCObjectType *objectType = pointerType->getObjectType();
  ObjCInterfaceDecl *iface = objectType->getInterface();

  // If we have a forward-declared type, we can't do this check.
  // Under ARC, it is an error not to have a forward-declared class.
  if (iface &&
      (getLangOpts().ObjCAutoRefCount
           ? SemaRef.RequireCompleteType(forLoc, QualType(objectType, 0),
                                         diag::err_arc_collection_forward,
                                         collection)
           : !SemaRef.isCompleteType(forLoc, QualType(objectType, 0)))) {
    // Otherwise, if we have any useful type information, check that
    // the type declares the appropriate method.
  } else if (iface || !objectType->qual_empty()) {
    const IdentifierInfo *selectorIdents[] = {
        &Context.Idents.get("countByEnumeratingWithState"),
        &Context.Idents.get("objects"), &Context.Idents.get("count")};
    Selector selector = Context.Selectors.getSelector(3, &selectorIdents[0]);

    ObjCMethodDecl *method = nullptr;

    // If there's an interface, look in both the public and private APIs.
    if (iface) {
      method = iface->lookupInstanceMethod(selector);
      if (!method)
        method = iface->lookupPrivateMethod(selector);
    }

    // Also check protocol qualifiers.
    if (!method)
      method = LookupMethodInQualifiedType(selector, pointerType,
                                           /*instance*/ true);

    // If we didn't find it anywhere, give up.
    if (!method) {
      Diag(forLoc, diag::warn_collection_expr_type)
          << collection->getType() << selector << collection->getSourceRange();
    }

    // TODO: check for an incompatible signature?
  }

  // Wrap up any cleanups in the expression.
  return collection;
}

StmtResult SemaObjC::FinishObjCForCollectionStmt(Stmt *S, Stmt *B) {
  if (!S || !B)
    return StmtError();
  ObjCForCollectionStmt *ForStmt = cast<ObjCForCollectionStmt>(S);

  ForStmt->setBody(B);
  return S;
}

StmtResult SemaObjC::ActOnObjCAtCatchStmt(SourceLocation AtLoc,
                                          SourceLocation RParen, Decl *Parm,
                                          Stmt *Body) {
  ASTContext &Context = getASTContext();
  VarDecl *Var = cast_or_null<VarDecl>(Parm);
  if (Var && Var->isInvalidDecl())
    return StmtError();

  return new (Context) ObjCAtCatchStmt(AtLoc, RParen, Var, Body);
}

StmtResult SemaObjC::ActOnObjCAtFinallyStmt(SourceLocation AtLoc, Stmt *Body) {
  ASTContext &Context = getASTContext();
  return new (Context) ObjCAtFinallyStmt(AtLoc, Body);
}

StmtResult SemaObjC::ActOnObjCAtTryStmt(SourceLocation AtLoc, Stmt *Try,
                                        MultiStmtArg CatchStmts,
                                        Stmt *Finally) {
  ASTContext &Context = getASTContext();
  if (!getLangOpts().ObjCExceptions)
    Diag(AtLoc, diag::err_objc_exceptions_disabled) << "@try";

  // Objective-C try is incompatible with SEH __try.
  sema::FunctionScopeInfo *FSI = SemaRef.getCurFunction();
  if (FSI->FirstSEHTryLoc.isValid()) {
    Diag(AtLoc, diag::err_mixing_cxx_try_seh_try) << 1;
    Diag(FSI->FirstSEHTryLoc, diag::note_conflicting_try_here) << "'__try'";
  }

  FSI->setHasObjCTry(AtLoc);
  unsigned NumCatchStmts = CatchStmts.size();
  return ObjCAtTryStmt::Create(Context, AtLoc, Try, CatchStmts.data(),
                               NumCatchStmts, Finally);
}

StmtResult SemaObjC::BuildObjCAtThrowStmt(SourceLocation AtLoc, Expr *Throw) {
  ASTContext &Context = getASTContext();
  if (Throw) {
    ExprResult Result = SemaRef.DefaultLvalueConversion(Throw);
    if (Result.isInvalid())
      return StmtError();

    Result =
        SemaRef.ActOnFinishFullExpr(Result.get(), /*DiscardedValue*/ false);
    if (Result.isInvalid())
      return StmtError();
    Throw = Result.get();

    QualType ThrowType = Throw->getType();
    // Make sure the expression type is an ObjC pointer or "void *".
    if (!ThrowType->isDependentType() &&
        !ThrowType->isObjCObjectPointerType()) {
      const PointerType *PT = ThrowType->getAs<PointerType>();
      if (!PT || !PT->getPointeeType()->isVoidType())
        return StmtError(Diag(AtLoc, diag::err_objc_throw_expects_object)
                         << Throw->getType() << Throw->getSourceRange());
    }
  }

  return new (Context) ObjCAtThrowStmt(AtLoc, Throw);
}

StmtResult SemaObjC::ActOnObjCAtThrowStmt(SourceLocation AtLoc, Expr *Throw,
                                          Scope *CurScope) {
  if (!getLangOpts().ObjCExceptions)
    Diag(AtLoc, diag::err_objc_exceptions_disabled) << "@throw";

  if (!Throw) {
    // @throw without an expression designates a rethrow (which must occur
    // in the context of an @catch clause).
    Scope *AtCatchParent = CurScope;
    while (AtCatchParent && !AtCatchParent->isAtCatchScope())
      AtCatchParent = AtCatchParent->getParent();
    if (!AtCatchParent)
      return StmtError(Diag(AtLoc, diag::err_rethrow_used_outside_catch));
  }
  return BuildObjCAtThrowStmt(AtLoc, Throw);
}

ExprResult SemaObjC::ActOnObjCAtSynchronizedOperand(SourceLocation atLoc,
                                                    Expr *operand) {
  ExprResult result = SemaRef.DefaultLvalueConversion(operand);
  if (result.isInvalid())
    return ExprError();
  operand = result.get();

  // Make sure the expression type is an ObjC pointer or "void *".
  QualType type = operand->getType();
  if (!type->isDependentType() && !type->isObjCObjectPointerType()) {
    const PointerType *pointerType = type->getAs<PointerType>();
    if (!pointerType || !pointerType->getPointeeType()->isVoidType()) {
      if (getLangOpts().CPlusPlus) {
        if (SemaRef.RequireCompleteType(atLoc, type,
                                        diag::err_incomplete_receiver_type))
          return Diag(atLoc, diag::err_objc_synchronized_expects_object)
                 << type << operand->getSourceRange();

        ExprResult result =
            SemaRef.PerformContextuallyConvertToObjCPointer(operand);
        if (result.isInvalid())
          return ExprError();
        if (!result.isUsable())
          return Diag(atLoc, diag::err_objc_synchronized_expects_object)
                 << type << operand->getSourceRange();

        operand = result.get();
      } else {
        return Diag(atLoc, diag::err_objc_synchronized_expects_object)
               << type << operand->getSourceRange();
      }
    }
  }

  // The operand to @synchronized is a full-expression.
  return SemaRef.ActOnFinishFullExpr(operand, /*DiscardedValue*/ false);
}

StmtResult SemaObjC::ActOnObjCAtSynchronizedStmt(SourceLocation AtLoc,
                                                 Expr *SyncExpr,
                                                 Stmt *SyncBody) {
  ASTContext &Context = getASTContext();
  // We can't jump into or indirect-jump out of a @synchronized block.
  SemaRef.setFunctionHasBranchProtectedScope();
  return new (Context) ObjCAtSynchronizedStmt(AtLoc, SyncExpr, SyncBody);
}

StmtResult SemaObjC::ActOnObjCAutoreleasePoolStmt(SourceLocation AtLoc,
                                                  Stmt *Body) {
  ASTContext &Context = getASTContext();
  SemaRef.setFunctionHasBranchProtectedScope();
  return new (Context) ObjCAutoreleasePoolStmt(AtLoc, Body);
}

TypeResult SemaObjC::actOnObjCProtocolQualifierType(
    SourceLocation lAngleLoc, ArrayRef<Decl *> protocols,
    ArrayRef<SourceLocation> protocolLocs, SourceLocation rAngleLoc) {
  ASTContext &Context = getASTContext();
  // Form id<protocol-list>.
  QualType Result = Context.getObjCObjectType(
      Context.ObjCBuiltinIdTy, {},
      llvm::ArrayRef((ObjCProtocolDecl *const *)protocols.data(),
                     protocols.size()),
      false);
  Result = Context.getObjCObjectPointerType(Result);

  TypeSourceInfo *ResultTInfo = Context.CreateTypeSourceInfo(Result);
  TypeLoc ResultTL = ResultTInfo->getTypeLoc();

  auto ObjCObjectPointerTL = ResultTL.castAs<ObjCObjectPointerTypeLoc>();
  ObjCObjectPointerTL.setStarLoc(SourceLocation()); // implicit

  auto ObjCObjectTL =
      ObjCObjectPointerTL.getPointeeLoc().castAs<ObjCObjectTypeLoc>();
  ObjCObjectTL.setHasBaseTypeAsWritten(false);
  ObjCObjectTL.getBaseLoc().initialize(Context, SourceLocation());

  // No type arguments.
  ObjCObjectTL.setTypeArgsLAngleLoc(SourceLocation());
  ObjCObjectTL.setTypeArgsRAngleLoc(SourceLocation());

  // Fill in protocol qualifiers.
  ObjCObjectTL.setProtocolLAngleLoc(lAngleLoc);
  ObjCObjectTL.setProtocolRAngleLoc(rAngleLoc);
  for (unsigned i = 0, n = protocols.size(); i != n; ++i)
    ObjCObjectTL.setProtocolLoc(i, protocolLocs[i]);

  // We're done. Return the completed type to the parser.
  return SemaRef.CreateParsedType(Result, ResultTInfo);
}

TypeResult SemaObjC::actOnObjCTypeArgsAndProtocolQualifiers(
    Scope *S, SourceLocation Loc, ParsedType BaseType,
    SourceLocation TypeArgsLAngleLoc, ArrayRef<ParsedType> TypeArgs,
    SourceLocation TypeArgsRAngleLoc, SourceLocation ProtocolLAngleLoc,
    ArrayRef<Decl *> Protocols, ArrayRef<SourceLocation> ProtocolLocs,
    SourceLocation ProtocolRAngleLoc) {
  ASTContext &Context = getASTContext();
  TypeSourceInfo *BaseTypeInfo = nullptr;
  QualType T = SemaRef.GetTypeFromParser(BaseType, &BaseTypeInfo);
  if (T.isNull())
    return true;

  // Handle missing type-source info.
  if (!BaseTypeInfo)
    BaseTypeInfo = Context.getTrivialTypeSourceInfo(T, Loc);

  // Extract type arguments.
  SmallVector<TypeSourceInfo *, 4> ActualTypeArgInfos;
  for (unsigned i = 0, n = TypeArgs.size(); i != n; ++i) {
    TypeSourceInfo *TypeArgInfo = nullptr;
    QualType TypeArg = SemaRef.GetTypeFromParser(TypeArgs[i], &TypeArgInfo);
    if (TypeArg.isNull()) {
      ActualTypeArgInfos.clear();
      break;
    }

    assert(TypeArgInfo && "No type source info?");
    ActualTypeArgInfos.push_back(TypeArgInfo);
  }

  // Build the object type.
  QualType Result = BuildObjCObjectType(
      T, BaseTypeInfo->getTypeLoc().getSourceRange().getBegin(),
      TypeArgsLAngleLoc, ActualTypeArgInfos, TypeArgsRAngleLoc,
      ProtocolLAngleLoc,
      llvm::ArrayRef((ObjCProtocolDecl *const *)Protocols.data(),
                     Protocols.size()),
      ProtocolLocs, ProtocolRAngleLoc,
      /*FailOnError=*/false,
      /*Rebuilding=*/false);

  if (Result == T)
    return BaseType;

  // Create source information for this type.
  TypeSourceInfo *ResultTInfo = Context.CreateTypeSourceInfo(Result);
  TypeLoc ResultTL = ResultTInfo->getTypeLoc();

  // For id<Proto1, Proto2> or Class<Proto1, Proto2>, we'll have an
  // object pointer type. Fill in source information for it.
  if (auto ObjCObjectPointerTL = ResultTL.getAs<ObjCObjectPointerTypeLoc>()) {
    // The '*' is implicit.
    ObjCObjectPointerTL.setStarLoc(SourceLocation());
    ResultTL = ObjCObjectPointerTL.getPointeeLoc();
  }

  if (auto OTPTL = ResultTL.getAs<ObjCTypeParamTypeLoc>()) {
    // Protocol qualifier information.
    if (OTPTL.getNumProtocols() > 0) {
      assert(OTPTL.getNumProtocols() == Protocols.size());
      OTPTL.setProtocolLAngleLoc(ProtocolLAngleLoc);
      OTPTL.setProtocolRAngleLoc(ProtocolRAngleLoc);
      for (unsigned i = 0, n = Protocols.size(); i != n; ++i)
        OTPTL.setProtocolLoc(i, ProtocolLocs[i]);
    }

    // We're done. Return the completed type to the parser.
    return SemaRef.CreateParsedType(Result, ResultTInfo);
  }

  auto ObjCObjectTL = ResultTL.castAs<ObjCObjectTypeLoc>();

  // Type argument information.
  if (ObjCObjectTL.getNumTypeArgs() > 0) {
    assert(ObjCObjectTL.getNumTypeArgs() == ActualTypeArgInfos.size());
    ObjCObjectTL.setTypeArgsLAngleLoc(TypeArgsLAngleLoc);
    ObjCObjectTL.setTypeArgsRAngleLoc(TypeArgsRAngleLoc);
    for (unsigned i = 0, n = ActualTypeArgInfos.size(); i != n; ++i)
      ObjCObjectTL.setTypeArgTInfo(i, ActualTypeArgInfos[i]);
  } else {
    ObjCObjectTL.setTypeArgsLAngleLoc(SourceLocation());
    ObjCObjectTL.setTypeArgsRAngleLoc(SourceLocation());
  }

  // Protocol qualifier information.
  if (ObjCObjectTL.getNumProtocols() > 0) {
    assert(ObjCObjectTL.getNumProtocols() == Protocols.size());
    ObjCObjectTL.setProtocolLAngleLoc(ProtocolLAngleLoc);
    ObjCObjectTL.setProtocolRAngleLoc(ProtocolRAngleLoc);
    for (unsigned i = 0, n = Protocols.size(); i != n; ++i)
      ObjCObjectTL.setProtocolLoc(i, ProtocolLocs[i]);
  } else {
    ObjCObjectTL.setProtocolLAngleLoc(SourceLocation());
    ObjCObjectTL.setProtocolRAngleLoc(SourceLocation());
  }

  // Base type.
  ObjCObjectTL.setHasBaseTypeAsWritten(true);
  if (ObjCObjectTL.getType() == T)
    ObjCObjectTL.getBaseLoc().initializeFullCopy(BaseTypeInfo->getTypeLoc());
  else
    ObjCObjectTL.getBaseLoc().initialize(Context, Loc);

  // We're done. Return the completed type to the parser.
  return SemaRef.CreateParsedType(Result, ResultTInfo);
}

QualType SemaObjC::BuildObjCTypeParamType(
    const ObjCTypeParamDecl *Decl, SourceLocation ProtocolLAngleLoc,
    ArrayRef<ObjCProtocolDecl *> Protocols,
    ArrayRef<SourceLocation> ProtocolLocs, SourceLocation ProtocolRAngleLoc,
    bool FailOnError) {
  ASTContext &Context = getASTContext();
  QualType Result = QualType(Decl->getTypeForDecl(), 0);
  if (!Protocols.empty()) {
    bool HasError;
    Result = Context.applyObjCProtocolQualifiers(Result, Protocols, HasError);
    if (HasError) {
      Diag(SourceLocation(), diag::err_invalid_protocol_qualifiers)
          << SourceRange(ProtocolLAngleLoc, ProtocolRAngleLoc);
      if (FailOnError)
        Result = QualType();
    }
    if (FailOnError && Result.isNull())
      return QualType();
  }

  return Result;
}

/// Apply Objective-C type arguments to the given type.
static QualType applyObjCTypeArgs(Sema &S, SourceLocation loc, QualType type,
                                  ArrayRef<TypeSourceInfo *> typeArgs,
                                  SourceRange typeArgsRange, bool failOnError,
                                  bool rebuilding) {
  // We can only apply type arguments to an Objective-C class type.
  const auto *objcObjectType = type->getAs<ObjCObjectType>();
  if (!objcObjectType || !objcObjectType->getInterface()) {
    S.Diag(loc, diag::err_objc_type_args_non_class) << type << typeArgsRange;

    if (failOnError)
      return QualType();
    return type;
  }

  // The class type must be parameterized.
  ObjCInterfaceDecl *objcClass = objcObjectType->getInterface();
  ObjCTypeParamList *typeParams = objcClass->getTypeParamList();
  if (!typeParams) {
    S.Diag(loc, diag::err_objc_type_args_non_parameterized_class)
        << objcClass->getDeclName() << FixItHint::CreateRemoval(typeArgsRange);

    if (failOnError)
      return QualType();

    return type;
  }

  // The type must not already be specialized.
  if (objcObjectType->isSpecialized()) {
    S.Diag(loc, diag::err_objc_type_args_specialized_class)
        << type << FixItHint::CreateRemoval(typeArgsRange);

    if (failOnError)
      return QualType();

    return type;
  }

  // Check the type arguments.
  SmallVector<QualType, 4> finalTypeArgs;
  unsigned numTypeParams = typeParams->size();
  bool anyPackExpansions = false;
  for (unsigned i = 0, n = typeArgs.size(); i != n; ++i) {
    TypeSourceInfo *typeArgInfo = typeArgs[i];
    QualType typeArg = typeArgInfo->getType();

    // Type arguments cannot have explicit qualifiers or nullability.
    // We ignore indirect sources of these, e.g. behind typedefs or
    // template arguments.
    if (TypeLoc qual = typeArgInfo->getTypeLoc().findExplicitQualifierLoc()) {
      bool diagnosed = false;
      SourceRange rangeToRemove;
      if (auto attr = qual.getAs<AttributedTypeLoc>()) {
        rangeToRemove = attr.getLocalSourceRange();
        if (attr.getTypePtr()->getImmediateNullability()) {
          typeArg = attr.getTypePtr()->getModifiedType();
          S.Diag(attr.getBeginLoc(),
                 diag::err_objc_type_arg_explicit_nullability)
              << typeArg << FixItHint::CreateRemoval(rangeToRemove);
          diagnosed = true;
        }
      }

      // When rebuilding, qualifiers might have gotten here through a
      // final substitution.
      if (!rebuilding && !diagnosed) {
        S.Diag(qual.getBeginLoc(), diag::err_objc_type_arg_qualified)
            << typeArg << typeArg.getQualifiers().getAsString()
            << FixItHint::CreateRemoval(rangeToRemove);
      }
    }

    // Remove qualifiers even if they're non-local.
    typeArg = typeArg.getUnqualifiedType();

    finalTypeArgs.push_back(typeArg);

    if (typeArg->getAs<PackExpansionType>())
      anyPackExpansions = true;

    // Find the corresponding type parameter, if there is one.
    ObjCTypeParamDecl *typeParam = nullptr;
    if (!anyPackExpansions) {
      if (i < numTypeParams) {
        typeParam = typeParams->begin()[i];
      } else {
        // Too many arguments.
        S.Diag(loc, diag::err_objc_type_args_wrong_arity)
            << false << objcClass->getDeclName() << (unsigned)typeArgs.size()
            << numTypeParams;
        S.Diag(objcClass->getLocation(), diag::note_previous_decl) << objcClass;

        if (failOnError)
          return QualType();

        return type;
      }
    }

    // Objective-C object pointer types must be substitutable for the bounds.
    if (const auto *typeArgObjC = typeArg->getAs<ObjCObjectPointerType>()) {
      // If we don't have a type parameter to match against, assume
      // everything is fine. There was a prior pack expansion that
      // means we won't be able to match anything.
      if (!typeParam) {
        assert(anyPackExpansions && "Too many arguments?");
        continue;
      }

      // Retrieve the bound.
      QualType bound = typeParam->getUnderlyingType();
      const auto *boundObjC = bound->castAs<ObjCObjectPointerType>();

      // Determine whether the type argument is substitutable for the bound.
      if (typeArgObjC->isObjCIdType()) {
        // When the type argument is 'id', the only acceptable type
        // parameter bound is 'id'.
        if (boundObjC->isObjCIdType())
          continue;
      } else if (S.Context.canAssignObjCInterfaces(boundObjC, typeArgObjC)) {
        // Otherwise, we follow the assignability rules.
        continue;
      }

      // Diagnose the mismatch.
      S.Diag(typeArgInfo->getTypeLoc().getBeginLoc(),
             diag::err_objc_type_arg_does_not_match_bound)
          << typeArg << bound << typeParam->getDeclName();
      S.Diag(typeParam->getLocation(), diag::note_objc_type_param_here)
          << typeParam->getDeclName();

      if (failOnError)
        return QualType();

      return type;
    }

    // Block pointer types are permitted for unqualified 'id' bounds.
    if (typeArg->isBlockPointerType()) {
      // If we don't have a type parameter to match against, assume
      // everything is fine. There was a prior pack expansion that
      // means we won't be able to match anything.
      if (!typeParam) {
        assert(anyPackExpansions && "Too many arguments?");
        continue;
      }

      // Retrieve the bound.
      QualType bound = typeParam->getUnderlyingType();
      if (bound->isBlockCompatibleObjCPointerType(S.Context))
        continue;

      // Diagnose the mismatch.
      S.Diag(typeArgInfo->getTypeLoc().getBeginLoc(),
             diag::err_objc_type_arg_does_not_match_bound)
          << typeArg << bound << typeParam->getDeclName();
      S.Diag(typeParam->getLocation(), diag::note_objc_type_param_here)
          << typeParam->getDeclName();

      if (failOnError)
        return QualType();

      return type;
    }

    // Types that have __attribute__((NSObject)) are permitted.
    if (typeArg->isObjCNSObjectType()) {
      continue;
    }

    // Dependent types will be checked at instantiation time.
    if (typeArg->isDependentType()) {
      continue;
    }

    // Diagnose non-id-compatible type arguments.
    S.Diag(typeArgInfo->getTypeLoc().getBeginLoc(),
           diag::err_objc_type_arg_not_id_compatible)
        << typeArg << typeArgInfo->getTypeLoc().getSourceRange();

    if (failOnError)
      return QualType();

    return type;
  }

  // Make sure we didn't have the wrong number of arguments.
  if (!anyPackExpansions && finalTypeArgs.size() != numTypeParams) {
    S.Diag(loc, diag::err_objc_type_args_wrong_arity)
        << (typeArgs.size() < typeParams->size()) << objcClass->getDeclName()
        << (unsigned)finalTypeArgs.size() << (unsigned)numTypeParams;
    S.Diag(objcClass->getLocation(), diag::note_previous_decl) << objcClass;

    if (failOnError)
      return QualType();

    return type;
  }

  // Success. Form the specialized type.
  return S.Context.getObjCObjectType(type, finalTypeArgs, {}, false);
}

QualType SemaObjC::BuildObjCObjectType(
    QualType BaseType, SourceLocation Loc, SourceLocation TypeArgsLAngleLoc,
    ArrayRef<TypeSourceInfo *> TypeArgs, SourceLocation TypeArgsRAngleLoc,
    SourceLocation ProtocolLAngleLoc, ArrayRef<ObjCProtocolDecl *> Protocols,
    ArrayRef<SourceLocation> ProtocolLocs, SourceLocation ProtocolRAngleLoc,
    bool FailOnError, bool Rebuilding) {
  ASTContext &Context = getASTContext();
  QualType Result = BaseType;
  if (!TypeArgs.empty()) {
    Result =
        applyObjCTypeArgs(SemaRef, Loc, Result, TypeArgs,
                          SourceRange(TypeArgsLAngleLoc, TypeArgsRAngleLoc),
                          FailOnError, Rebuilding);
    if (FailOnError && Result.isNull())
      return QualType();
  }

  if (!Protocols.empty()) {
    bool HasError;
    Result = Context.applyObjCProtocolQualifiers(Result, Protocols, HasError);
    if (HasError) {
      Diag(Loc, diag::err_invalid_protocol_qualifiers)
          << SourceRange(ProtocolLAngleLoc, ProtocolRAngleLoc);
      if (FailOnError)
        Result = QualType();
    }
    if (FailOnError && Result.isNull())
      return QualType();
  }

  return Result;
}

ParsedType SemaObjC::ActOnObjCInstanceType(SourceLocation Loc) {
  ASTContext &Context = getASTContext();
  QualType T = Context.getObjCInstanceType();
  TypeSourceInfo *TInfo = Context.getTrivialTypeSourceInfo(T, Loc);
  return SemaRef.CreateParsedType(T, TInfo);
}

//===--- CHECK: Objective-C retain cycles ----------------------------------//

namespace {

struct RetainCycleOwner {
  VarDecl *Variable = nullptr;
  SourceRange Range;
  SourceLocation Loc;
  bool Indirect = false;

  RetainCycleOwner() = default;

  void setLocsFrom(Expr *e) {
    Loc = e->getExprLoc();
    Range = e->getSourceRange();
  }
};

} // namespace

/// Consider whether capturing the given variable can possibly lead to
/// a retain cycle.
static bool considerVariable(VarDecl *var, Expr *ref, RetainCycleOwner &owner) {
  // In ARC, it's captured strongly iff the variable has __strong
  // lifetime.  In MRR, it's captured strongly if the variable is
  // __block and has an appropriate type.
  if (var->getType().getObjCLifetime() != Qualifiers::OCL_Strong)
    return false;

  owner.Variable = var;
  if (ref)
    owner.setLocsFrom(ref);
  return true;
}

static bool findRetainCycleOwner(Sema &S, Expr *e, RetainCycleOwner &owner) {
  while (true) {
    e = e->IgnoreParens();
    if (CastExpr *cast = dyn_cast<CastExpr>(e)) {
      switch (cast->getCastKind()) {
      case CK_BitCast:
      case CK_LValueBitCast:
      case CK_LValueToRValue:
      case CK_ARCReclaimReturnedObject:
        e = cast->getSubExpr();
        continue;

      default:
        return false;
      }
    }

    if (ObjCIvarRefExpr *ref = dyn_cast<ObjCIvarRefExpr>(e)) {
      ObjCIvarDecl *ivar = ref->getDecl();
      if (ivar->getType().getObjCLifetime() != Qualifiers::OCL_Strong)
        return false;

      // Try to find a retain cycle in the base.
      if (!findRetainCycleOwner(S, ref->getBase(), owner))
        return false;

      if (ref->isFreeIvar())
        owner.setLocsFrom(ref);
      owner.Indirect = true;
      return true;
    }

    if (DeclRefExpr *ref = dyn_cast<DeclRefExpr>(e)) {
      VarDecl *var = dyn_cast<VarDecl>(ref->getDecl());
      if (!var)
        return false;
      return considerVariable(var, ref, owner);
    }

    if (MemberExpr *member = dyn_cast<MemberExpr>(e)) {
      if (member->isArrow())
        return false;

      // Don't count this as an indirect ownership.
      e = member->getBase();
      continue;
    }

    if (PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(e)) {
      // Only pay attention to pseudo-objects on property references.
      ObjCPropertyRefExpr *pre = dyn_cast<ObjCPropertyRefExpr>(
          pseudo->getSyntacticForm()->IgnoreParens());
      if (!pre)
        return false;
      if (pre->isImplicitProperty())
        return false;
      ObjCPropertyDecl *property = pre->getExplicitProperty();
      if (!property->isRetaining() &&
          !(property->getPropertyIvarDecl() &&
            property->getPropertyIvarDecl()->getType().getObjCLifetime() ==
                Qualifiers::OCL_Strong))
        return false;

      owner.Indirect = true;
      if (pre->isSuperReceiver()) {
        owner.Variable = S.getCurMethodDecl()->getSelfDecl();
        if (!owner.Variable)
          return false;
        owner.Loc = pre->getLocation();
        owner.Range = pre->getSourceRange();
        return true;
      }
      e = const_cast<Expr *>(
          cast<OpaqueValueExpr>(pre->getBase())->getSourceExpr());
      continue;
    }

    // Array ivars?

    return false;
  }
}

namespace {

struct FindCaptureVisitor : EvaluatedExprVisitor<FindCaptureVisitor> {
  VarDecl *Variable;
  Expr *Capturer = nullptr;
  bool VarWillBeReased = false;

  FindCaptureVisitor(ASTContext &Context, VarDecl *variable)
      : EvaluatedExprVisitor<FindCaptureVisitor>(Context), Variable(variable) {}

  void VisitDeclRefExpr(DeclRefExpr *ref) {
    if (ref->getDecl() == Variable && !Capturer)
      Capturer = ref;
  }

  void VisitObjCIvarRefExpr(ObjCIvarRefExpr *ref) {
    if (Capturer)
      return;
    Visit(ref->getBase());
    if (Capturer && ref->isFreeIvar())
      Capturer = ref;
  }

  void VisitBlockExpr(BlockExpr *block) {
    // Look inside nested blocks
    if (block->getBlockDecl()->capturesVariable(Variable))
      Visit(block->getBlockDecl()->getBody());
  }

  void VisitOpaqueValueExpr(OpaqueValueExpr *OVE) {
    if (Capturer)
      return;
    if (OVE->getSourceExpr())
      Visit(OVE->getSourceExpr());
  }

  void VisitBinaryOperator(BinaryOperator *BinOp) {
    if (!Variable || VarWillBeReased || BinOp->getOpcode() != BO_Assign)
      return;
    Expr *LHS = BinOp->getLHS();
    if (const DeclRefExpr *DRE = dyn_cast_or_null<DeclRefExpr>(LHS)) {
      if (DRE->getDecl() != Variable)
        return;
      if (Expr *RHS = BinOp->getRHS()) {
        RHS = RHS->IgnoreParenCasts();
        std::optional<llvm::APSInt> Value;
        VarWillBeReased =
            (RHS && (Value = RHS->getIntegerConstantExpr(Context)) &&
             *Value == 0);
      }
    }
  }
};

} // namespace

/// Check whether the given argument is a block which captures a
/// variable.
static Expr *findCapturingExpr(Sema &S, Expr *e, RetainCycleOwner &owner) {
  assert(owner.Variable && owner.Loc.isValid());

  e = e->IgnoreParenCasts();

  // Look through [^{...} copy] and Block_copy(^{...}).
  if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(e)) {
    Selector Cmd = ME->getSelector();
    if (Cmd.isUnarySelector() && Cmd.getNameForSlot(0) == "copy") {
      e = ME->getInstanceReceiver();
      if (!e)
        return nullptr;
      e = e->IgnoreParenCasts();
    }
  } else if (CallExpr *CE = dyn_cast<CallExpr>(e)) {
    if (CE->getNumArgs() == 1) {
      FunctionDecl *Fn = dyn_cast_or_null<FunctionDecl>(CE->getCalleeDecl());
      if (Fn) {
        const IdentifierInfo *FnI = Fn->getIdentifier();
        if (FnI && FnI->isStr("_Block_copy")) {
          e = CE->getArg(0)->IgnoreParenCasts();
        }
      }
    }
  }

  BlockExpr *block = dyn_cast<BlockExpr>(e);
  if (!block || !block->getBlockDecl()->capturesVariable(owner.Variable))
    return nullptr;

  FindCaptureVisitor visitor(S.Context, owner.Variable);
  visitor.Visit(block->getBlockDecl()->getBody());
  return visitor.VarWillBeReased ? nullptr : visitor.Capturer;
}

static void diagnoseRetainCycle(Sema &S, Expr *capturer,
                                RetainCycleOwner &owner) {
  assert(capturer);
  assert(owner.Variable && owner.Loc.isValid());

  S.Diag(capturer->getExprLoc(), diag::warn_arc_retain_cycle)
      << owner.Variable << capturer->getSourceRange();
  S.Diag(owner.Loc, diag::note_arc_retain_cycle_owner)
      << owner.Indirect << owner.Range;
}

/// Check for a keyword selector that starts with the word 'add' or
/// 'set'.
static bool isSetterLikeSelector(Selector sel) {
  if (sel.isUnarySelector())
    return false;

  StringRef str = sel.getNameForSlot(0);
  str = str.ltrim('_');
  if (str.starts_with("set"))
    str = str.substr(3);
  else if (str.starts_with("add")) {
    // Specially allow 'addOperationWithBlock:'.
    if (sel.getNumArgs() == 1 && str.starts_with("addOperationWithBlock"))
      return false;
    str = str.substr(3);
  } else
    return false;

  if (str.empty())
    return true;
  return !isLowercase(str.front());
}

static std::optional<int>
GetNSMutableArrayArgumentIndex(SemaObjC &S, ObjCMessageExpr *Message) {
  bool IsMutableArray = S.NSAPIObj->isSubclassOfNSClass(
      Message->getReceiverInterface(), NSAPI::ClassId_NSMutableArray);
  if (!IsMutableArray) {
    return std::nullopt;
  }

  Selector Sel = Message->getSelector();

  std::optional<NSAPI::NSArrayMethodKind> MKOpt =
      S.NSAPIObj->getNSArrayMethodKind(Sel);
  if (!MKOpt) {
    return std::nullopt;
  }

  NSAPI::NSArrayMethodKind MK = *MKOpt;

  switch (MK) {
  case NSAPI::NSMutableArr_addObject:
  case NSAPI::NSMutableArr_insertObjectAtIndex:
  case NSAPI::NSMutableArr_setObjectAtIndexedSubscript:
    return 0;
  case NSAPI::NSMutableArr_replaceObjectAtIndex:
    return 1;

  default:
    return std::nullopt;
  }

  return std::nullopt;
}

static std::optional<int>
GetNSMutableDictionaryArgumentIndex(SemaObjC &S, ObjCMessageExpr *Message) {
  bool IsMutableDictionary = S.NSAPIObj->isSubclassOfNSClass(
      Message->getReceiverInterface(), NSAPI::ClassId_NSMutableDictionary);
  if (!IsMutableDictionary) {
    return std::nullopt;
  }

  Selector Sel = Message->getSelector();

  std::optional<NSAPI::NSDictionaryMethodKind> MKOpt =
      S.NSAPIObj->getNSDictionaryMethodKind(Sel);
  if (!MKOpt) {
    return std::nullopt;
  }

  NSAPI::NSDictionaryMethodKind MK = *MKOpt;

  switch (MK) {
  case NSAPI::NSMutableDict_setObjectForKey:
  case NSAPI::NSMutableDict_setValueForKey:
  case NSAPI::NSMutableDict_setObjectForKeyedSubscript:
    return 0;

  default:
    return std::nullopt;
  }

  return std::nullopt;
}

static std::optional<int> GetNSSetArgumentIndex(SemaObjC &S,
                                                ObjCMessageExpr *Message) {
  bool IsMutableSet = S.NSAPIObj->isSubclassOfNSClass(
      Message->getReceiverInterface(), NSAPI::ClassId_NSMutableSet);

  bool IsMutableOrderedSet = S.NSAPIObj->isSubclassOfNSClass(
      Message->getReceiverInterface(), NSAPI::ClassId_NSMutableOrderedSet);
  if (!IsMutableSet && !IsMutableOrderedSet) {
    return std::nullopt;
  }

  Selector Sel = Message->getSelector();

  std::optional<NSAPI::NSSetMethodKind> MKOpt =
      S.NSAPIObj->getNSSetMethodKind(Sel);
  if (!MKOpt) {
    return std::nullopt;
  }

  NSAPI::NSSetMethodKind MK = *MKOpt;

  switch (MK) {
  case NSAPI::NSMutableSet_addObject:
  case NSAPI::NSOrderedSet_setObjectAtIndex:
  case NSAPI::NSOrderedSet_setObjectAtIndexedSubscript:
  case NSAPI::NSOrderedSet_insertObjectAtIndex:
    return 0;
  case NSAPI::NSOrderedSet_replaceObjectAtIndexWithObject:
    return 1;
  }

  return std::nullopt;
}

void SemaObjC::CheckObjCCircularContainer(ObjCMessageExpr *Message) {
  if (!Message->isInstanceMessage()) {
    return;
  }

  std::optional<int> ArgOpt;

  if (!(ArgOpt = GetNSMutableArrayArgumentIndex(*this, Message)) &&
      !(ArgOpt = GetNSMutableDictionaryArgumentIndex(*this, Message)) &&
      !(ArgOpt = GetNSSetArgumentIndex(*this, Message))) {
    return;
  }

  int ArgIndex = *ArgOpt;

  Expr *Arg = Message->getArg(ArgIndex)->IgnoreImpCasts();
  if (OpaqueValueExpr *OE = dyn_cast<OpaqueValueExpr>(Arg)) {
    Arg = OE->getSourceExpr()->IgnoreImpCasts();
  }

  if (Message->getReceiverKind() == ObjCMessageExpr::SuperInstance) {
    if (DeclRefExpr *ArgRE = dyn_cast<DeclRefExpr>(Arg)) {
      if (ArgRE->isObjCSelfExpr()) {
        Diag(Message->getSourceRange().getBegin(),
             diag::warn_objc_circular_container)
            << ArgRE->getDecl() << StringRef("'super'");
      }
    }
  } else {
    Expr *Receiver = Message->getInstanceReceiver()->IgnoreImpCasts();

    if (OpaqueValueExpr *OE = dyn_cast<OpaqueValueExpr>(Receiver)) {
      Receiver = OE->getSourceExpr()->IgnoreImpCasts();
    }

    if (DeclRefExpr *ReceiverRE = dyn_cast<DeclRefExpr>(Receiver)) {
      if (DeclRefExpr *ArgRE = dyn_cast<DeclRefExpr>(Arg)) {
        if (ReceiverRE->getDecl() == ArgRE->getDecl()) {
          ValueDecl *Decl = ReceiverRE->getDecl();
          Diag(Message->getSourceRange().getBegin(),
               diag::warn_objc_circular_container)
              << Decl << Decl;
          if (!ArgRE->isObjCSelfExpr()) {
            Diag(Decl->getLocation(),
                 diag::note_objc_circular_container_declared_here)
                << Decl;
          }
        }
      }
    } else if (ObjCIvarRefExpr *IvarRE = dyn_cast<ObjCIvarRefExpr>(Receiver)) {
      if (ObjCIvarRefExpr *IvarArgRE = dyn_cast<ObjCIvarRefExpr>(Arg)) {
        if (IvarRE->getDecl() == IvarArgRE->getDecl()) {
          ObjCIvarDecl *Decl = IvarRE->getDecl();
          Diag(Message->getSourceRange().getBegin(),
               diag::warn_objc_circular_container)
              << Decl << Decl;
          Diag(Decl->getLocation(),
               diag::note_objc_circular_container_declared_here)
              << Decl;
        }
      }
    }
  }
}

/// Check a message send to see if it's likely to cause a retain cycle.
void SemaObjC::checkRetainCycles(ObjCMessageExpr *msg) {
  // Only check instance methods whose selector looks like a setter.
  if (!msg->isInstanceMessage() || !isSetterLikeSelector(msg->getSelector()))
    return;

  // Try to find a variable that the receiver is strongly owned by.
  RetainCycleOwner owner;
  if (msg->getReceiverKind() == ObjCMessageExpr::Instance) {
    if (!findRetainCycleOwner(SemaRef, msg->getInstanceReceiver(), owner))
      return;
  } else {
    assert(msg->getReceiverKind() == ObjCMessageExpr::SuperInstance);
    owner.Variable = SemaRef.getCurMethodDecl()->getSelfDecl();
    owner.Loc = msg->getSuperLoc();
    owner.Range = msg->getSuperLoc();
  }

  // Check whether the receiver is captured by any of the arguments.
  const ObjCMethodDecl *MD = msg->getMethodDecl();
  for (unsigned i = 0, e = msg->getNumArgs(); i != e; ++i) {
    if (Expr *capturer = findCapturingExpr(SemaRef, msg->getArg(i), owner)) {
      // noescape blocks should not be retained by the method.
      if (MD && MD->parameters()[i]->hasAttr<NoEscapeAttr>())
        continue;
      return diagnoseRetainCycle(SemaRef, capturer, owner);
    }
  }
}

/// Check a property assign to see if it's likely to cause a retain cycle.
void SemaObjC::checkRetainCycles(Expr *receiver, Expr *argument) {
  RetainCycleOwner owner;
  if (!findRetainCycleOwner(SemaRef, receiver, owner))
    return;

  if (Expr *capturer = findCapturingExpr(SemaRef, argument, owner))
    diagnoseRetainCycle(SemaRef, capturer, owner);
}

void SemaObjC::checkRetainCycles(VarDecl *Var, Expr *Init) {
  RetainCycleOwner Owner;
  if (!considerVariable(Var, /*DeclRefExpr=*/nullptr, Owner))
    return;

  // Because we don't have an expression for the variable, we have to set the
  // location explicitly here.
  Owner.Loc = Var->getLocation();
  Owner.Range = Var->getSourceRange();

  if (Expr *Capturer = findCapturingExpr(SemaRef, Init, Owner))
    diagnoseRetainCycle(SemaRef, Capturer, Owner);
}

/// CheckObjCString - Checks that the argument to the builtin
/// CFString constructor is correct
/// Note: It might also make sense to do the UTF-16 conversion here (would
/// simplify the backend).
bool SemaObjC::CheckObjCString(Expr *Arg) {
  Arg = Arg->IgnoreParenCasts();
  StringLiteral *Literal = dyn_cast<StringLiteral>(Arg);

  if (!Literal || !Literal->isOrdinary()) {
    Diag(Arg->getBeginLoc(), diag::err_cfstring_literal_not_string_constant)
        << Arg->getSourceRange();
    return true;
  }

  if (Literal->containsNonAsciiOrNull()) {
    StringRef String = Literal->getString();
    unsigned NumBytes = String.size();
    SmallVector<llvm::UTF16, 128> ToBuf(NumBytes);
    const llvm::UTF8 *FromPtr = (const llvm::UTF8 *)String.data();
    llvm::UTF16 *ToPtr = &ToBuf[0];

    llvm::ConversionResult Result =
        llvm::ConvertUTF8toUTF16(&FromPtr, FromPtr + NumBytes, &ToPtr,
                                 ToPtr + NumBytes, llvm::strictConversion);
    // Check for conversion failure.
    if (Result != llvm::conversionOK)
      Diag(Arg->getBeginLoc(), diag::warn_cfstring_truncated)
          << Arg->getSourceRange();
  }
  return false;
}

bool SemaObjC::CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation lbrac,
                                   ArrayRef<const Expr *> Args) {
  VariadicCallType CallType = Method->isVariadic()
                                  ? VariadicCallType::Method
                                  : VariadicCallType::DoesNotApply;

  SemaRef.checkCall(Method, nullptr, /*ThisArg=*/nullptr, Args,
                    /*IsMemberFunction=*/false, lbrac, Method->getSourceRange(),
                    CallType);

  SemaRef.CheckTCBEnforcement(lbrac, Method);

  return false;
}

const DeclContext *SemaObjC::getCurObjCLexicalContext() const {
  const DeclContext *DC = SemaRef.getCurLexicalContext();
  // A category implicitly has the attribute of the interface.
  if (const ObjCCategoryDecl *CatD = dyn_cast<ObjCCategoryDecl>(DC))
    DC = CatD->getClassInterface();
  return DC;
}

/// Retrieve the identifier "NSError".
IdentifierInfo *SemaObjC::getNSErrorIdent() {
  if (!Ident_NSError)
    Ident_NSError = SemaRef.PP.getIdentifierInfo("NSError");

  return Ident_NSError;
}

void SemaObjC::ActOnObjCContainerStartDefinition(ObjCContainerDecl *IDecl) {
  assert(
      IDecl->getLexicalParent() == SemaRef.CurContext &&
      "The next DeclContext should be lexically contained in the current one.");
  SemaRef.CurContext = IDecl;
}

void SemaObjC::ActOnObjCContainerFinishDefinition() {
  // Exit this scope of this interface definition.
  SemaRef.PopDeclContext();
}

void SemaObjC::ActOnObjCTemporaryExitContainerContext(
    ObjCContainerDecl *ObjCCtx) {
  assert(ObjCCtx == SemaRef.CurContext && "Mismatch of container contexts");
  SemaRef.OriginalLexicalContext = ObjCCtx;
  ActOnObjCContainerFinishDefinition();
}

void SemaObjC::ActOnObjCReenterContainerContext(ObjCContainerDecl *ObjCCtx) {
  ActOnObjCContainerStartDefinition(ObjCCtx);
  SemaRef.OriginalLexicalContext = nullptr;
}

/// Find the protocol with the given name, if any.
ObjCProtocolDecl *SemaObjC::LookupProtocol(IdentifierInfo *II,
                                           SourceLocation IdLoc,
                                           RedeclarationKind Redecl) {
  Decl *D = SemaRef.LookupSingleName(SemaRef.TUScope, II, IdLoc,
                                     Sema::LookupObjCProtocolName, Redecl);
  return cast_or_null<ObjCProtocolDecl>(D);
}

/// Determine whether this is an Objective-C writeback conversion,
/// used for parameter passing when performing automatic reference counting.
///
/// \param FromType The type we're converting form.
///
/// \param ToType The type we're converting to.
///
/// \param ConvertedType The type that will be produced after applying
/// this conversion.
bool SemaObjC::isObjCWritebackConversion(QualType FromType, QualType ToType,
                                         QualType &ConvertedType) {
  ASTContext &Context = getASTContext();
  if (!getLangOpts().ObjCAutoRefCount ||
      Context.hasSameUnqualifiedType(FromType, ToType))
    return false;

  // Parameter must be a pointer to __autoreleasing (with no other qualifiers).
  QualType ToPointee;
  if (const PointerType *ToPointer = ToType->getAs<PointerType>())
    ToPointee = ToPointer->getPointeeType();
  else
    return false;

  Qualifiers ToQuals = ToPointee.getQualifiers();
  if (!ToPointee->isObjCLifetimeType() ||
      ToQuals.getObjCLifetime() != Qualifiers::OCL_Autoreleasing ||
      !ToQuals.withoutObjCLifetime().empty())
    return false;

  // Argument must be a pointer to __strong to __weak.
  QualType FromPointee;
  if (const PointerType *FromPointer = FromType->getAs<PointerType>())
    FromPointee = FromPointer->getPointeeType();
  else
    return false;

  Qualifiers FromQuals = FromPointee.getQualifiers();
  if (!FromPointee->isObjCLifetimeType() ||
      (FromQuals.getObjCLifetime() != Qualifiers::OCL_Strong &&
       FromQuals.getObjCLifetime() != Qualifiers::OCL_Weak))
    return false;

  // Make sure that we have compatible qualifiers.
  FromQuals.setObjCLifetime(Qualifiers::OCL_Autoreleasing);
  if (!ToQuals.compatiblyIncludes(FromQuals, getASTContext()))
    return false;

  // Remove qualifiers from the pointee type we're converting from; they
  // aren't used in the compatibility check belong, and we'll be adding back
  // qualifiers (with __autoreleasing) if the compatibility check succeeds.
  FromPointee = FromPointee.getUnqualifiedType();

  // The unqualified form of the pointee types must be compatible.
  ToPointee = ToPointee.getUnqualifiedType();
  bool IncompatibleObjC;
  if (Context.typesAreCompatible(FromPointee, ToPointee))
    FromPointee = ToPointee;
  else if (!SemaRef.isObjCPointerConversion(FromPointee, ToPointee, FromPointee,
                                            IncompatibleObjC))
    return false;

  /// Construct the type we're converting to, which is a pointer to
  /// __autoreleasing pointee.
  FromPointee = Context.getQualifiedType(FromPointee, FromQuals);
  ConvertedType = Context.getPointerType(FromPointee);
  return true;
}

/// CheckSubscriptingKind - This routine decide what type
/// of indexing represented by "FromE" is being done.
SemaObjC::ObjCSubscriptKind SemaObjC::CheckSubscriptingKind(Expr *FromE) {
  // If the expression already has integral or enumeration type, we're golden.
  QualType T = FromE->getType();
  if (T->isIntegralOrEnumerationType())
    return SemaObjC::OS_Array;

  // If we don't have a class type in C++, there's no way we can get an
  // expression of integral or enumeration type.
  const RecordType *RecordTy = T->getAs<RecordType>();
  if (!RecordTy && (T->isObjCObjectPointerType() || T->isVoidPointerType()))
    // All other scalar cases are assumed to be dictionary indexing which
    // caller handles, with diagnostics if needed.
    return SemaObjC::OS_Dictionary;
  if (!getLangOpts().CPlusPlus || !RecordTy || RecordTy->isIncompleteType()) {
    // No indexing can be done. Issue diagnostics and quit.
    const Expr *IndexExpr = FromE->IgnoreParenImpCasts();
    if (isa<StringLiteral>(IndexExpr))
      Diag(FromE->getExprLoc(), diag::err_objc_subscript_pointer)
          << T << FixItHint::CreateInsertion(FromE->getExprLoc(), "@");
    else
      Diag(FromE->getExprLoc(), diag::err_objc_subscript_type_conversion) << T;
    return SemaObjC::OS_Error;
  }

  // We must have a complete class type.
  if (SemaRef.RequireCompleteType(FromE->getExprLoc(), T,
                                  diag::err_objc_index_incomplete_class_type,
                                  FromE))
    return SemaObjC::OS_Error;

  // Look for a conversion to an integral, enumeration type, or
  // objective-C pointer type.
  int NoIntegrals = 0, NoObjCIdPointers = 0;
  SmallVector<CXXConversionDecl *, 4> ConversionDecls;

  for (NamedDecl *D : cast<CXXRecordDecl>(RecordTy->getDecl())
                          ->getVisibleConversionFunctions()) {
    if (CXXConversionDecl *Conversion =
            dyn_cast<CXXConversionDecl>(D->getUnderlyingDecl())) {
      QualType CT = Conversion->getConversionType().getNonReferenceType();
      if (CT->isIntegralOrEnumerationType()) {
        ++NoIntegrals;
        ConversionDecls.push_back(Conversion);
      } else if (CT->isObjCIdType() || CT->isBlockPointerType()) {
        ++NoObjCIdPointers;
        ConversionDecls.push_back(Conversion);
      }
    }
  }
  if (NoIntegrals == 1 && NoObjCIdPointers == 0)
    return SemaObjC::OS_Array;
  if (NoIntegrals == 0 && NoObjCIdPointers == 1)
    return SemaObjC::OS_Dictionary;
  if (NoIntegrals == 0 && NoObjCIdPointers == 0) {
    // No conversion function was found. Issue diagnostic and return.
    Diag(FromE->getExprLoc(), diag::err_objc_subscript_type_conversion)
        << FromE->getType();
    return SemaObjC::OS_Error;
  }
  Diag(FromE->getExprLoc(), diag::err_objc_multiple_subscript_type_conversion)
      << FromE->getType();
  for (unsigned int i = 0; i < ConversionDecls.size(); i++)
    Diag(ConversionDecls[i]->getLocation(),
         diag::note_conv_function_declared_at);

  return SemaObjC::OS_Error;
}

void SemaObjC::AddCFAuditedAttribute(Decl *D) {
  ASTContext &Context = getASTContext();
  auto IdLoc = SemaRef.PP.getPragmaARCCFCodeAuditedInfo();
  if (!IdLoc.getLoc().isValid())
    return;

  // Don't add a redundant or conflicting attribute.
  if (D->hasAttr<CFAuditedTransferAttr>() ||
      D->hasAttr<CFUnknownTransferAttr>())
    return;

  AttributeCommonInfo Info(IdLoc.getIdentifierInfo(),
                           SourceRange(IdLoc.getLoc()),
                           AttributeCommonInfo::Form::Pragma());
  D->addAttr(CFAuditedTransferAttr::CreateImplicit(Context, Info));
}

bool SemaObjC::isCFError(RecordDecl *RD) {
  // If we already know about CFError, test it directly.
  if (CFError)
    return CFError == RD;

  // Check whether this is CFError, which we identify based on its bridge to
  // NSError. CFErrorRef used to be declared with "objc_bridge" but is now
  // declared with "objc_bridge_mutable", so look for either one of the two
  // attributes.
  if (RD->getTagKind() == TagTypeKind::Struct) {
    IdentifierInfo *bridgedType = nullptr;
    if (auto bridgeAttr = RD->getAttr<ObjCBridgeAttr>())
      bridgedType = bridgeAttr->getBridgedType();
    else if (auto bridgeAttr = RD->getAttr<ObjCBridgeMutableAttr>())
      bridgedType = bridgeAttr->getBridgedType();

    if (bridgedType == getNSErrorIdent()) {
      CFError = RD;
      return true;
    }
  }

  return false;
}

bool SemaObjC::isNSStringType(QualType T, bool AllowNSAttributedString) {
  const auto *PT = T->getAs<ObjCObjectPointerType>();
  if (!PT)
    return false;

  ObjCInterfaceDecl *Cls = PT->getObjectType()->getInterface();
  if (!Cls)
    return false;

  IdentifierInfo *ClsName = Cls->getIdentifier();

  if (AllowNSAttributedString &&
      ClsName == &getASTContext().Idents.get("NSAttributedString"))
    return true;
  // FIXME: Should we walk the chain of classes?
  return ClsName == &getASTContext().Idents.get("NSString") ||
         ClsName == &getASTContext().Idents.get("NSMutableString");
}

bool SemaObjC::isCFStringType(QualType T) {
  const auto *PT = T->getAs<PointerType>();
  if (!PT)
    return false;

  const auto *RT = PT->getPointeeType()->getAs<RecordType>();
  if (!RT)
    return false;

  const RecordDecl *RD = RT->getDecl();
  if (RD->getTagKind() != TagTypeKind::Struct)
    return false;

  return RD->getIdentifier() == &getASTContext().Idents.get("__CFString");
}

static bool checkIBOutletCommon(Sema &S, Decl *D, const ParsedAttr &AL) {
  // The IBOutlet/IBOutletCollection attributes only apply to instance
  // variables or properties of Objective-C classes.  The outlet must also
  // have an object reference type.
  if (const auto *VD = dyn_cast<ObjCIvarDecl>(D)) {
    if (!VD->getType()->getAs<ObjCObjectPointerType>()) {
      S.Diag(AL.getLoc(), diag::warn_iboutlet_object_type)
          << AL << VD->getType() << 0;
      return false;
    }
  } else if (const auto *PD = dyn_cast<ObjCPropertyDecl>(D)) {
    if (!PD->getType()->getAs<ObjCObjectPointerType>()) {
      S.Diag(AL.getLoc(), diag::warn_iboutlet_object_type)
          << AL << PD->getType() << 1;
      return false;
    }
  } else {
    S.Diag(AL.getLoc(), diag::warn_attribute_iboutlet) << AL;
    return false;
  }

  return true;
}

void SemaObjC::handleIBOutlet(Decl *D, const ParsedAttr &AL) {
  if (!checkIBOutletCommon(SemaRef, D, AL))
    return;

  D->addAttr(::new (getASTContext()) IBOutletAttr(getASTContext(), AL));
}

void SemaObjC::handleIBOutletCollection(Decl *D, const ParsedAttr &AL) {

  ASTContext &Context = getASTContext();
  // The iboutletcollection attribute can have zero or one arguments.
  if (AL.getNumArgs() > 1) {
    Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1;
    return;
  }

  if (!checkIBOutletCommon(SemaRef, D, AL))
    return;

  ParsedType PT;

  if (AL.hasParsedType())
    PT = AL.getTypeArg();
  else {
    PT = SemaRef.getTypeName(
        Context.Idents.get("NSObject"), AL.getLoc(),
        SemaRef.getScopeForContext(D->getDeclContext()->getParent()));
    if (!PT) {
      Diag(AL.getLoc(), diag::err_iboutletcollection_type) << "NSObject";
      return;
    }
  }

  TypeSourceInfo *QTLoc = nullptr;
  QualType QT = SemaRef.GetTypeFromParser(PT, &QTLoc);
  if (!QTLoc)
    QTLoc = Context.getTrivialTypeSourceInfo(QT, AL.getLoc());

  // Diagnose use of non-object type in iboutletcollection attribute.
  // FIXME. Gnu attribute extension ignores use of builtin types in
  // attributes. So, __attribute__((iboutletcollection(char))) will be
  // treated as __attribute__((iboutletcollection())).
  if (!QT->isObjCIdType() && !QT->isObjCObjectType()) {
    Diag(AL.getLoc(), QT->isBuiltinType()
                          ? diag::err_iboutletcollection_builtintype
                          : diag::err_iboutletcollection_type)
        << QT;
    return;
  }

  D->addAttr(::new (Context) IBOutletCollectionAttr(Context, AL, QTLoc));
}

void SemaObjC::handleSuppresProtocolAttr(Decl *D, const ParsedAttr &AL) {
  if (!cast<ObjCProtocolDecl>(D)->isThisDeclarationADefinition()) {
    Diag(AL.getLoc(), diag::err_objc_attr_protocol_requires_definition)
        << AL << AL.getRange();
    return;
  }

  D->addAttr(::new (getASTContext())
                 ObjCExplicitProtocolImplAttr(getASTContext(), AL));
}

void SemaObjC::handleDirectAttr(Decl *D, const ParsedAttr &AL) {
  // objc_direct cannot be set on methods declared in the context of a protocol
  if (isa<ObjCProtocolDecl>(D->getDeclContext())) {
    Diag(AL.getLoc(), diag::err_objc_direct_on_protocol) << false;
    return;
  }

  if (getLangOpts().ObjCRuntime.allowsDirectDispatch()) {
    handleSimpleAttribute<ObjCDirectAttr>(*this, D, AL);
  } else {
    Diag(AL.getLoc(), diag::warn_objc_direct_ignored) << AL;
  }
}

void SemaObjC::handleDirectMembersAttr(Decl *D, const ParsedAttr &AL) {
  if (getLangOpts().ObjCRuntime.allowsDirectDispatch()) {
    handleSimpleAttribute<ObjCDirectMembersAttr>(*this, D, AL);
  } else {
    Diag(AL.getLoc(), diag::warn_objc_direct_ignored) << AL;
  }
}

void SemaObjC::handleMethodFamilyAttr(Decl *D, const ParsedAttr &AL) {
  const auto *M = cast<ObjCMethodDecl>(D);
  if (!AL.isArgIdent(0)) {
    Diag(AL.getLoc(), diag::err_attribute_argument_n_type)
        << AL << 1 << AANT_ArgumentIdentifier;
    return;
  }

  IdentifierLoc *IL = AL.getArgAsIdent(0);
  ObjCMethodFamilyAttr::FamilyKind F;
  if (!ObjCMethodFamilyAttr::ConvertStrToFamilyKind(
          IL->getIdentifierInfo()->getName(), F)) {
    Diag(IL->getLoc(), diag::warn_attribute_type_not_supported)
        << AL << IL->getIdentifierInfo();
    return;
  }

  if (F == ObjCMethodFamilyAttr::OMF_init &&
      !M->getReturnType()->isObjCObjectPointerType()) {
    Diag(M->getLocation(), diag::err_init_method_bad_return_type)
        << M->getReturnType();
    // Ignore the attribute.
    return;
  }

  D->addAttr(new (getASTContext())
                 ObjCMethodFamilyAttr(getASTContext(), AL, F));
}

void SemaObjC::handleNSObject(Decl *D, const ParsedAttr &AL) {
  if (const auto *TD = dyn_cast<TypedefNameDecl>(D)) {
    QualType T = TD->getUnderlyingType();
    if (!T->isCARCBridgableType()) {
      Diag(TD->getLocation(), diag::err_nsobject_attribute);
      return;
    }
  } else if (const auto *PD = dyn_cast<ObjCPropertyDecl>(D)) {
    QualType T = PD->getType();
    if (!T->isCARCBridgableType()) {
      Diag(PD->getLocation(), diag::err_nsobject_attribute);
      return;
    }
  } else {
    // It is okay to include this attribute on properties, e.g.:
    //
    //  @property (retain, nonatomic) struct Bork *Q __attribute__((NSObject));
    //
    // In this case it follows tradition and suppresses an error in the above
    // case.
    Diag(D->getLocation(), diag::warn_nsobject_attribute);
  }
  D->addAttr(::new (getASTContext()) ObjCNSObjectAttr(getASTContext(), AL));
}

void SemaObjC::handleIndependentClass(Decl *D, const ParsedAttr &AL) {
  if (const auto *TD = dyn_cast<TypedefNameDecl>(D)) {
    QualType T = TD->getUnderlyingType();
    if (!T->isObjCObjectPointerType()) {
      Diag(TD->getLocation(), diag::warn_ptr_independentclass_attribute);
      return;
    }
  } else {
    Diag(D->getLocation(), diag::warn_independentclass_attribute);
    return;
  }
  D->addAttr(::new (getASTContext())
                 ObjCIndependentClassAttr(getASTContext(), AL));
}

void SemaObjC::handleBlocksAttr(Decl *D, const ParsedAttr &AL) {
  if (!AL.isArgIdent(0)) {
    Diag(AL.getLoc(), diag::err_attribute_argument_n_type)
        << AL << 1 << AANT_ArgumentIdentifier;
    return;
  }

  IdentifierInfo *II = AL.getArgAsIdent(0)->getIdentifierInfo();
  BlocksAttr::BlockType type;
  if (!BlocksAttr::ConvertStrToBlockType(II->getName(), type)) {
    Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) << AL << II;
    return;
  }

  D->addAttr(::new (getASTContext()) BlocksAttr(getASTContext(), AL, type));
}

static bool isValidSubjectOfNSReturnsRetainedAttribute(QualType QT) {
  return QT->isDependentType() || QT->isObjCRetainableType();
}

static bool isValidSubjectOfNSAttribute(QualType QT) {
  return QT->isDependentType() || QT->isObjCObjectPointerType() ||
         QT->isObjCNSObjectType();
}

static bool isValidSubjectOfCFAttribute(QualType QT) {
  return QT->isDependentType() || QT->isPointerType() ||
         isValidSubjectOfNSAttribute(QT);
}

static bool isValidSubjectOfOSAttribute(QualType QT) {
  if (QT->isDependentType())
    return true;
  QualType PT = QT->getPointeeType();
  return !PT.isNull() && PT->getAsCXXRecordDecl() != nullptr;
}

void SemaObjC::AddXConsumedAttr(Decl *D, const AttributeCommonInfo &CI,
                                Sema::RetainOwnershipKind K,
                                bool IsTemplateInstantiation) {
  ValueDecl *VD = cast<ValueDecl>(D);
  switch (K) {
  case Sema::RetainOwnershipKind::OS:
    handleSimpleAttributeOrDiagnose<OSConsumedAttr>(
        *this, VD, CI, isValidSubjectOfOSAttribute(VD->getType()),
        diag::warn_ns_attribute_wrong_parameter_type,
        /*ExtraArgs=*/CI.getRange(), "os_consumed", /*pointers*/ 1);
    return;
  case Sema::RetainOwnershipKind::NS:
    handleSimpleAttributeOrDiagnose<NSConsumedAttr>(
        *this, VD, CI, isValidSubjectOfNSAttribute(VD->getType()),

        // These attributes are normally just advisory, but in ARC, ns_consumed
        // is significant.  Allow non-dependent code to contain inappropriate
        // attributes even in ARC, but require template instantiations to be
        // set up correctly.
        ((IsTemplateInstantiation && getLangOpts().ObjCAutoRefCount)
             ? diag::err_ns_attribute_wrong_parameter_type
             : diag::warn_ns_attribute_wrong_parameter_type),
        /*ExtraArgs=*/CI.getRange(), "ns_consumed", /*objc pointers*/ 0);
    return;
  case Sema::RetainOwnershipKind::CF:
    handleSimpleAttributeOrDiagnose<CFConsumedAttr>(
        *this, VD, CI, isValidSubjectOfCFAttribute(VD->getType()),
        diag::warn_ns_attribute_wrong_parameter_type,
        /*ExtraArgs=*/CI.getRange(), "cf_consumed", /*pointers*/ 1);
    return;
  }
}

Sema::RetainOwnershipKind
SemaObjC::parsedAttrToRetainOwnershipKind(const ParsedAttr &AL) {
  switch (AL.getKind()) {
  case ParsedAttr::AT_CFConsumed:
  case ParsedAttr::AT_CFReturnsRetained:
  case ParsedAttr::AT_CFReturnsNotRetained:
    return Sema::RetainOwnershipKind::CF;
  case ParsedAttr::AT_OSConsumesThis:
  case ParsedAttr::AT_OSConsumed:
  case ParsedAttr::AT_OSReturnsRetained:
  case ParsedAttr::AT_OSReturnsNotRetained:
  case ParsedAttr::AT_OSReturnsRetainedOnZero:
  case ParsedAttr::AT_OSReturnsRetainedOnNonZero:
    return Sema::RetainOwnershipKind::OS;
  case ParsedAttr::AT_NSConsumesSelf:
  case ParsedAttr::AT_NSConsumed:
  case ParsedAttr::AT_NSReturnsRetained:
  case ParsedAttr::AT_NSReturnsNotRetained:
  case ParsedAttr::AT_NSReturnsAutoreleased:
    return Sema::RetainOwnershipKind::NS;
  default:
    llvm_unreachable("Wrong argument supplied");
  }
}

bool SemaObjC::checkNSReturnsRetainedReturnType(SourceLocation Loc,
                                                QualType QT) {
  if (isValidSubjectOfNSReturnsRetainedAttribute(QT))
    return false;

  Diag(Loc, diag::warn_ns_attribute_wrong_return_type)
      << "'ns_returns_retained'" << 0 << 0;
  return true;
}

/// \return whether the parameter is a pointer to OSObject pointer.
bool SemaObjC::isValidOSObjectOutParameter(const Decl *D) {
  const auto *PVD = dyn_cast<ParmVarDecl>(D);
  if (!PVD)
    return false;
  QualType QT = PVD->getType();
  QualType PT = QT->getPointeeType();
  return !PT.isNull() && isValidSubjectOfOSAttribute(PT);
}

void SemaObjC::handleXReturnsXRetainedAttr(Decl *D, const ParsedAttr &AL) {
  QualType ReturnType;
  Sema::RetainOwnershipKind K = parsedAttrToRetainOwnershipKind(AL);

  if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
    ReturnType = MD->getReturnType();
  } else if (getLangOpts().ObjCAutoRefCount && hasDeclarator(D) &&
             (AL.getKind() == ParsedAttr::AT_NSReturnsRetained)) {
    return; // ignore: was handled as a type attribute
  } else if (const auto *PD = dyn_cast<ObjCPropertyDecl>(D)) {
    ReturnType = PD->getType();
  } else if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
    ReturnType = FD->getReturnType();
  } else if (const auto *Param = dyn_cast<ParmVarDecl>(D)) {
    // Attributes on parameters are used for out-parameters,
    // passed as pointers-to-pointers.
    unsigned DiagID = K == Sema::RetainOwnershipKind::CF
                          ? /*pointer-to-CF-pointer*/ 2
                          : /*pointer-to-OSObject-pointer*/ 3;
    ReturnType = Param->getType()->getPointeeType();
    if (ReturnType.isNull()) {
      Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_parameter_type)
          << AL << DiagID << AL.getRange();
      return;
    }
  } else if (AL.isUsedAsTypeAttr()) {
    return;
  } else {
    AttributeDeclKind ExpectedDeclKind;
    switch (AL.getKind()) {
    default:
      llvm_unreachable("invalid ownership attribute");
    case ParsedAttr::AT_NSReturnsRetained:
    case ParsedAttr::AT_NSReturnsAutoreleased:
    case ParsedAttr::AT_NSReturnsNotRetained:
      ExpectedDeclKind = ExpectedFunctionOrMethod;
      break;

    case ParsedAttr::AT_OSReturnsRetained:
    case ParsedAttr::AT_OSReturnsNotRetained:
    case ParsedAttr::AT_CFReturnsRetained:
    case ParsedAttr::AT_CFReturnsNotRetained:
      ExpectedDeclKind = ExpectedFunctionMethodOrParameter;
      break;
    }
    Diag(D->getBeginLoc(), diag::warn_attribute_wrong_decl_type)
        << AL.getRange() << AL << AL.isRegularKeywordAttribute()
        << ExpectedDeclKind;
    return;
  }

  bool TypeOK;
  bool Cf;
  unsigned ParmDiagID = 2; // Pointer-to-CF-pointer
  switch (AL.getKind()) {
  default:
    llvm_unreachable("invalid ownership attribute");
  case ParsedAttr::AT_NSReturnsRetained:
    TypeOK = isValidSubjectOfNSReturnsRetainedAttribute(ReturnType);
    Cf = false;
    break;

  case ParsedAttr::AT_NSReturnsAutoreleased:
  case ParsedAttr::AT_NSReturnsNotRetained:
    TypeOK = isValidSubjectOfNSAttribute(ReturnType);
    Cf = false;
    break;

  case ParsedAttr::AT_CFReturnsRetained:
  case ParsedAttr::AT_CFReturnsNotRetained:
    TypeOK = isValidSubjectOfCFAttribute(ReturnType);
    Cf = true;
    break;

  case ParsedAttr::AT_OSReturnsRetained:
  case ParsedAttr::AT_OSReturnsNotRetained:
    TypeOK = isValidSubjectOfOSAttribute(ReturnType);
    Cf = true;
    ParmDiagID = 3; // Pointer-to-OSObject-pointer
    break;
  }

  if (!TypeOK) {
    if (AL.isUsedAsTypeAttr())
      return;

    if (isa<ParmVarDecl>(D)) {
      Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_parameter_type)
          << AL << ParmDiagID << AL.getRange();
    } else {
      // Needs to be kept in sync with warn_ns_attribute_wrong_return_type.
      enum : unsigned { Function, Method, Property } SubjectKind = Function;
      if (isa<ObjCMethodDecl>(D))
        SubjectKind = Method;
      else if (isa<ObjCPropertyDecl>(D))
        SubjectKind = Property;
      Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_return_type)
          << AL << SubjectKind << Cf << AL.getRange();
    }
    return;
  }

  switch (AL.getKind()) {
  default:
    llvm_unreachable("invalid ownership attribute");
  case ParsedAttr::AT_NSReturnsAutoreleased:
    handleSimpleAttribute<NSReturnsAutoreleasedAttr>(*this, D, AL);
    return;
  case ParsedAttr::AT_CFReturnsNotRetained:
    handleSimpleAttribute<CFReturnsNotRetainedAttr>(*this, D, AL);
    return;
  case ParsedAttr::AT_NSReturnsNotRetained:
    handleSimpleAttribute<NSReturnsNotRetainedAttr>(*this, D, AL);
    return;
  case ParsedAttr::AT_CFReturnsRetained:
    handleSimpleAttribute<CFReturnsRetainedAttr>(*this, D, AL);
    return;
  case ParsedAttr::AT_NSReturnsRetained:
    handleSimpleAttribute<NSReturnsRetainedAttr>(*this, D, AL);
    return;
  case ParsedAttr::AT_OSReturnsRetained:
    handleSimpleAttribute<OSReturnsRetainedAttr>(*this, D, AL);
    return;
  case ParsedAttr::AT_OSReturnsNotRetained:
    handleSimpleAttribute<OSReturnsNotRetainedAttr>(*this, D, AL);
    return;
  };
}

void SemaObjC::handleReturnsInnerPointerAttr(Decl *D, const ParsedAttr &Attrs) {
  const int EP_ObjCMethod = 1;
  const int EP_ObjCProperty = 2;

  SourceLocation loc = Attrs.getLoc();
  QualType resultType;
  if (isa<ObjCMethodDecl>(D))
    resultType = cast<ObjCMethodDecl>(D)->getReturnType();
  else
    resultType = cast<ObjCPropertyDecl>(D)->getType();

  if (!resultType->isReferenceType() &&
      (!resultType->isPointerType() || resultType->isObjCRetainableType())) {
    Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_return_type)
        << SourceRange(loc) << Attrs
        << (isa<ObjCMethodDecl>(D) ? EP_ObjCMethod : EP_ObjCProperty)
        << /*non-retainable pointer*/ 2;

    // Drop the attribute.
    return;
  }

  D->addAttr(::new (getASTContext())
                 ObjCReturnsInnerPointerAttr(getASTContext(), Attrs));
}

void SemaObjC::handleRequiresSuperAttr(Decl *D, const ParsedAttr &Attrs) {
  const auto *Method = cast<ObjCMethodDecl>(D);

  const DeclContext *DC = Method->getDeclContext();
  if (const auto *PDecl = dyn_cast_if_present<ObjCProtocolDecl>(DC)) {
    Diag(D->getBeginLoc(), diag::warn_objc_requires_super_protocol)
        << Attrs << 0;
    Diag(PDecl->getLocation(), diag::note_protocol_decl);
    return;
  }
  if (Method->getMethodFamily() == OMF_dealloc) {
    Diag(D->getBeginLoc(), diag::warn_objc_requires_super_protocol)
        << Attrs << 1;
    return;
  }

  D->addAttr(::new (getASTContext())
                 ObjCRequiresSuperAttr(getASTContext(), Attrs));
}

void SemaObjC::handleNSErrorDomain(Decl *D, const ParsedAttr &Attr) {
  if (!isa<TagDecl>(D)) {
    Diag(D->getBeginLoc(), diag::err_nserrordomain_invalid_decl) << 0;
    return;
  }

  IdentifierLoc *IdentLoc =
      Attr.isArgIdent(0) ? Attr.getArgAsIdent(0) : nullptr;
  if (!IdentLoc || !IdentLoc->getIdentifierInfo()) {
    // Try to locate the argument directly.
    SourceLocation Loc = Attr.getLoc();
    if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0))
      Loc = Attr.getArgAsExpr(0)->getBeginLoc();

    Diag(Loc, diag::err_nserrordomain_invalid_decl) << 0;
    return;
  }

  // Verify that the identifier is a valid decl in the C decl namespace.
  LookupResult Result(SemaRef, DeclarationName(IdentLoc->getIdentifierInfo()),
                      SourceLocation(),
                      Sema::LookupNameKind::LookupOrdinaryName);
  if (!SemaRef.LookupName(Result, SemaRef.TUScope) ||
      !Result.getAsSingle<VarDecl>()) {
    Diag(IdentLoc->getLoc(), diag::err_nserrordomain_invalid_decl)
        << 1 << IdentLoc->getIdentifierInfo();
    return;
  }

  D->addAttr(::new (getASTContext()) NSErrorDomainAttr(
      getASTContext(), Attr, IdentLoc->getIdentifierInfo()));
}

void SemaObjC::handleBridgeAttr(Decl *D, const ParsedAttr &AL) {
  IdentifierLoc *Parm = AL.isArgIdent(0) ? AL.getArgAsIdent(0) : nullptr;

  if (!Parm) {
    Diag(D->getBeginLoc(), diag::err_objc_attr_not_id) << AL << 0;
    return;
  }

  // Typedefs only allow objc_bridge(id) and have some additional checking.
  if (const auto *TD = dyn_cast<TypedefNameDecl>(D)) {
    if (!Parm->getIdentifierInfo()->isStr("id")) {
      Diag(AL.getLoc(), diag::err_objc_attr_typedef_not_id) << AL;
      return;
    }

    // Only allow 'cv void *'.
    QualType T = TD->getUnderlyingType();
    if (!T->isVoidPointerType()) {
      Diag(AL.getLoc(), diag::err_objc_attr_typedef_not_void_pointer);
      return;
    }
  }

  D->addAttr(::new (getASTContext()) ObjCBridgeAttr(getASTContext(), AL,
                                                    Parm->getIdentifierInfo()));
}

void SemaObjC::handleBridgeMutableAttr(Decl *D, const ParsedAttr &AL) {
  IdentifierLoc *Parm = AL.isArgIdent(0) ? AL.getArgAsIdent(0) : nullptr;

  if (!Parm) {
    Diag(D->getBeginLoc(), diag::err_objc_attr_not_id) << AL << 0;
    return;
  }

  D->addAttr(::new (getASTContext()) ObjCBridgeMutableAttr(
      getASTContext(), AL, Parm->getIdentifierInfo()));
}

void SemaObjC::handleBridgeRelatedAttr(Decl *D, const ParsedAttr &AL) {
  IdentifierInfo *RelatedClass =
      AL.isArgIdent(0) ? AL.getArgAsIdent(0)->getIdentifierInfo() : nullptr;
  if (!RelatedClass) {
    Diag(D->getBeginLoc(), diag::err_objc_attr_not_id) << AL << 0;
    return;
  }
  IdentifierInfo *ClassMethod =
      AL.getArgAsIdent(1) ? AL.getArgAsIdent(1)->getIdentifierInfo() : nullptr;
  IdentifierInfo *InstanceMethod =
      AL.getArgAsIdent(2) ? AL.getArgAsIdent(2)->getIdentifierInfo() : nullptr;
  D->addAttr(::new (getASTContext()) ObjCBridgeRelatedAttr(
      getASTContext(), AL, RelatedClass, ClassMethod, InstanceMethod));
}

void SemaObjC::handleDesignatedInitializer(Decl *D, const ParsedAttr &AL) {
  DeclContext *Ctx = D->getDeclContext();

  // This attribute can only be applied to methods in interfaces or class
  // extensions.
  if (!isa<ObjCInterfaceDecl>(Ctx) &&
      !(isa<ObjCCategoryDecl>(Ctx) &&
        cast<ObjCCategoryDecl>(Ctx)->IsClassExtension())) {
    Diag(D->getLocation(), diag::err_designated_init_attr_non_init);
    return;
  }

  ObjCInterfaceDecl *IFace;
  if (auto *CatDecl = dyn_cast<ObjCCategoryDecl>(Ctx))
    IFace = CatDecl->getClassInterface();
  else
    IFace = cast<ObjCInterfaceDecl>(Ctx);

  if (!IFace)
    return;

  IFace->setHasDesignatedInitializers();
  D->addAttr(::new (getASTContext())
                 ObjCDesignatedInitializerAttr(getASTContext(), AL));
}

void SemaObjC::handleRuntimeName(Decl *D, const ParsedAttr &AL) {
  StringRef MetaDataName;
  if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, MetaDataName))
    return;
  D->addAttr(::new (getASTContext())
                 ObjCRuntimeNameAttr(getASTContext(), AL, MetaDataName));
}

// When a user wants to use objc_boxable with a union or struct
// but they don't have access to the declaration (legacy/third-party code)
// then they can 'enable' this feature with a typedef:
// typedef struct __attribute((objc_boxable)) legacy_struct legacy_struct;
void SemaObjC::handleBoxable(Decl *D, const ParsedAttr &AL) {
  bool notify = false;

  auto *RD = dyn_cast<RecordDecl>(D);
  if (RD && RD->getDefinition()) {
    RD = RD->getDefinition();
    notify = true;
  }

  if (RD) {
    ObjCBoxableAttr *BoxableAttr =
        ::new (getASTContext()) ObjCBoxableAttr(getASTContext(), AL);
    RD->addAttr(BoxableAttr);
    if (notify) {
      // we need to notify ASTReader/ASTWriter about
      // modification of existing declaration
      if (ASTMutationListener *L = SemaRef.getASTMutationListener())
        L->AddedAttributeToRecord(BoxableAttr, RD);
    }
  }
}

void SemaObjC::handleOwnershipAttr(Decl *D, const ParsedAttr &AL) {
  if (hasDeclarator(D))
    return;

  Diag(D->getBeginLoc(), diag::err_attribute_wrong_decl_type)
      << AL.getRange() << AL << AL.isRegularKeywordAttribute()
      << ExpectedVariable;
}

void SemaObjC::handlePreciseLifetimeAttr(Decl *D, const ParsedAttr &AL) {
  const auto *VD = cast<ValueDecl>(D);
  QualType QT = VD->getType();

  if (!QT->isDependentType() && !QT->isObjCLifetimeType()) {
    Diag(AL.getLoc(), diag::err_objc_precise_lifetime_bad_type) << QT;
    return;
  }

  Qualifiers::ObjCLifetime Lifetime = QT.getObjCLifetime();

  // If we have no lifetime yet, check the lifetime we're presumably
  // going to infer.
  if (Lifetime == Qualifiers::OCL_None && !QT->isDependentType())
    Lifetime = QT->getObjCARCImplicitLifetime();

  switch (Lifetime) {
  case Qualifiers::OCL_None:
    assert(QT->isDependentType() &&
           "didn't infer lifetime for non-dependent type?");
    break;

  case Qualifiers::OCL_Weak:   // meaningful
  case Qualifiers::OCL_Strong: // meaningful
    break;

  case Qualifiers::OCL_ExplicitNone:
  case Qualifiers::OCL_Autoreleasing:
    Diag(AL.getLoc(), diag::warn_objc_precise_lifetime_meaningless)
        << (Lifetime == Qualifiers::OCL_Autoreleasing);
    break;
  }

  D->addAttr(::new (getASTContext())
                 ObjCPreciseLifetimeAttr(getASTContext(), AL));
}

static bool tryMakeVariablePseudoStrong(Sema &S, VarDecl *VD,
                                        bool DiagnoseFailure) {
  QualType Ty = VD->getType();
  if (!Ty->isObjCRetainableType()) {
    if (DiagnoseFailure) {
      S.Diag(VD->getBeginLoc(), diag::warn_ignored_objc_externally_retained)
          << 0;
    }
    return false;
  }

  Qualifiers::ObjCLifetime LifetimeQual = Ty.getQualifiers().getObjCLifetime();

  // SemaObjC::inferObjCARCLifetime must run after processing decl attributes
  // (because __block lowers to an attribute), so if the lifetime hasn't been
  // explicitly specified, infer it locally now.
  if (LifetimeQual == Qualifiers::OCL_None)
    LifetimeQual = Ty->getObjCARCImplicitLifetime();

  // The attributes only really makes sense for __strong variables; ignore any
  // attempts to annotate a parameter with any other lifetime qualifier.
  if (LifetimeQual != Qualifiers::OCL_Strong) {
    if (DiagnoseFailure) {
      S.Diag(VD->getBeginLoc(), diag::warn_ignored_objc_externally_retained)
          << 1;
    }
    return false;
  }

  // Tampering with the type of a VarDecl here is a bit of a hack, but we need
  // to ensure that the variable is 'const' so that we can error on
  // modification, which can otherwise over-release.
  VD->setType(Ty.withConst());
  VD->setARCPseudoStrong(true);
  return true;
}

void SemaObjC::handleExternallyRetainedAttr(Decl *D, const ParsedAttr &AL) {
  if (auto *VD = dyn_cast<VarDecl>(D)) {
    assert(!isa<ParmVarDecl>(VD) && "should be diagnosed automatically");
    if (!VD->hasLocalStorage()) {
      Diag(D->getBeginLoc(), diag::warn_ignored_objc_externally_retained) << 0;
      return;
    }

    if (!tryMakeVariablePseudoStrong(SemaRef, VD, /*DiagnoseFailure=*/true))
      return;

    handleSimpleAttribute<ObjCExternallyRetainedAttr>(*this, D, AL);
    return;
  }

  // If D is a function-like declaration (method, block, or function), then we
  // make every parameter psuedo-strong.
  unsigned NumParams =
      hasFunctionProto(D) ? getFunctionOrMethodNumParams(D) : 0;
  for (unsigned I = 0; I != NumParams; ++I) {
    auto *PVD = const_cast<ParmVarDecl *>(getFunctionOrMethodParam(D, I));
    QualType Ty = PVD->getType();

    // If a user wrote a parameter with __strong explicitly, then assume they
    // want "real" strong semantics for that parameter. This works because if
    // the parameter was written with __strong, then the strong qualifier will
    // be non-local.
    if (Ty.getLocalUnqualifiedType().getQualifiers().getObjCLifetime() ==
        Qualifiers::OCL_Strong)
      continue;

    tryMakeVariablePseudoStrong(SemaRef, PVD, /*DiagnoseFailure=*/false);
  }
  handleSimpleAttribute<ObjCExternallyRetainedAttr>(*this, D, AL);
}

bool SemaObjC::GetFormatNSStringIdx(const FormatAttr *Format, unsigned &Idx) {
  Sema::FormatStringInfo FSI;
  if ((SemaRef.GetFormatStringType(Format) == FormatStringType::NSString) &&
      SemaRef.getFormatStringInfo(Format->getFormatIdx(), Format->getFirstArg(),
                                  false, true, &FSI)) {
    Idx = FSI.FormatIdx;
    return true;
  }
  return false;
}

/// Diagnose use of %s directive in an NSString which is being passed
/// as formatting string to formatting method.
void SemaObjC::DiagnoseCStringFormatDirectiveInCFAPI(const NamedDecl *FDecl,
                                                     Expr **Args,
                                                     unsigned NumArgs) {
  unsigned Idx = 0;
  bool Format = false;
  ObjCStringFormatFamily SFFamily = FDecl->getObjCFStringFormattingFamily();
  if (SFFamily == ObjCStringFormatFamily::SFF_CFString) {
    Idx = 2;
    Format = true;
  } else
    for (const auto *I : FDecl->specific_attrs<FormatAttr>()) {
      if (GetFormatNSStringIdx(I, Idx)) {
        Format = true;
        break;
      }
    }
  if (!Format || NumArgs <= Idx)
    return;
  const Expr *FormatExpr = Args[Idx];
  if (const CStyleCastExpr *CSCE = dyn_cast<CStyleCastExpr>(FormatExpr))
    FormatExpr = CSCE->getSubExpr();
  const StringLiteral *FormatString;
  if (const ObjCStringLiteral *OSL =
          dyn_cast<ObjCStringLiteral>(FormatExpr->IgnoreParenImpCasts()))
    FormatString = OSL->getString();
  else
    FormatString = dyn_cast<StringLiteral>(FormatExpr->IgnoreParenImpCasts());
  if (!FormatString)
    return;
  if (SemaRef.FormatStringHasSArg(FormatString)) {
    Diag(FormatExpr->getExprLoc(), diag::warn_objc_cdirective_format_string)
        << "%s" << 1 << 1;
    Diag(FDecl->getLocation(), diag::note_entity_declared_at)
        << FDecl->getDeclName();
  }
}

bool SemaObjC::isSignedCharBool(QualType Ty) {
  return Ty->isSpecificBuiltinType(BuiltinType::SChar) && getLangOpts().ObjC &&
         NSAPIObj->isObjCBOOLType(Ty);
}

void SemaObjC::adornBoolConversionDiagWithTernaryFixit(
    const Expr *SourceExpr, const Sema::SemaDiagnosticBuilder &Builder) {
  const Expr *Ignored = SourceExpr->IgnoreImplicit();
  if (const auto *OVE = dyn_cast<OpaqueValueExpr>(Ignored))
    Ignored = OVE->getSourceExpr();
  bool NeedsParens = isa<AbstractConditionalOperator>(Ignored) ||
                     isa<BinaryOperator>(Ignored) ||
                     isa<CXXOperatorCallExpr>(Ignored);
  SourceLocation EndLoc = SemaRef.getLocForEndOfToken(SourceExpr->getEndLoc());
  if (NeedsParens)
    Builder << FixItHint::CreateInsertion(SourceExpr->getBeginLoc(), "(")
            << FixItHint::CreateInsertion(EndLoc, ")");
  Builder << FixItHint::CreateInsertion(EndLoc, " ? YES : NO");
}

/// Check a single element within a collection literal against the
/// target element type.
static void checkCollectionLiteralElement(Sema &S, QualType TargetElementType,
                                          Expr *Element, unsigned ElementKind) {
  // Skip a bitcast to 'id' or qualified 'id'.
  if (auto ICE = dyn_cast<ImplicitCastExpr>(Element)) {
    if (ICE->getCastKind() == CK_BitCast &&
        ICE->getSubExpr()->getType()->getAs<ObjCObjectPointerType>())
      Element = ICE->getSubExpr();
  }

  QualType ElementType = Element->getType();
  ExprResult ElementResult(Element);
  if (ElementType->getAs<ObjCObjectPointerType>() &&
      !S.IsAssignConvertCompatible(S.CheckSingleAssignmentConstraints(
          TargetElementType, ElementResult, false, false))) {
    S.Diag(Element->getBeginLoc(), diag::warn_objc_collection_literal_element)
        << ElementType << ElementKind << TargetElementType
        << Element->getSourceRange();
  }

  if (auto ArrayLiteral = dyn_cast<ObjCArrayLiteral>(Element))
    S.ObjC().checkArrayLiteral(TargetElementType, ArrayLiteral);
  else if (auto DictionaryLiteral = dyn_cast<ObjCDictionaryLiteral>(Element))
    S.ObjC().checkDictionaryLiteral(TargetElementType, DictionaryLiteral);
}

/// Check an Objective-C array literal being converted to the given
/// target type.
void SemaObjC::checkArrayLiteral(QualType TargetType,
                                 ObjCArrayLiteral *ArrayLiteral) {
  if (!NSArrayDecl)
    return;

  const auto *TargetObjCPtr = TargetType->getAs<ObjCObjectPointerType>();
  if (!TargetObjCPtr)
    return;

  if (TargetObjCPtr->isUnspecialized() ||
      TargetObjCPtr->getInterfaceDecl()->getCanonicalDecl() !=
          NSArrayDecl->getCanonicalDecl())
    return;

  auto TypeArgs = TargetObjCPtr->getTypeArgs();
  if (TypeArgs.size() != 1)
    return;

  QualType TargetElementType = TypeArgs[0];
  for (unsigned I = 0, N = ArrayLiteral->getNumElements(); I != N; ++I) {
    checkCollectionLiteralElement(SemaRef, TargetElementType,
                                  ArrayLiteral->getElement(I), 0);
  }
}

void SemaObjC::checkDictionaryLiteral(
    QualType TargetType, ObjCDictionaryLiteral *DictionaryLiteral) {
  if (!NSDictionaryDecl)
    return;

  const auto *TargetObjCPtr = TargetType->getAs<ObjCObjectPointerType>();
  if (!TargetObjCPtr)
    return;

  if (TargetObjCPtr->isUnspecialized() ||
      TargetObjCPtr->getInterfaceDecl()->getCanonicalDecl() !=
          NSDictionaryDecl->getCanonicalDecl())
    return;

  auto TypeArgs = TargetObjCPtr->getTypeArgs();
  if (TypeArgs.size() != 2)
    return;

  QualType TargetKeyType = TypeArgs[0];
  QualType TargetObjectType = TypeArgs[1];
  for (unsigned I = 0, N = DictionaryLiteral->getNumElements(); I != N; ++I) {
    auto Element = DictionaryLiteral->getKeyValueElement(I);
    checkCollectionLiteralElement(SemaRef, TargetKeyType, Element.Key, 1);
    checkCollectionLiteralElement(SemaRef, TargetObjectType, Element.Value, 2);
  }
}

} // namespace clang
