static void diagSyntacticUseRestrictions()

in lib/Sema/MiscDiagnostics.cpp [105:1327]


static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
                                         bool isExprStmt) {
  class DiagnoseWalker : public BaseDiagnosticWalker {
    SmallPtrSet<Expr*, 4> AlreadyDiagnosedMetatypes;
    SmallPtrSet<DeclRefExpr*, 4> AlreadyDiagnosedBitCasts;

    bool IsExprStmt;

  public:
    ASTContext &Ctx;
    const DeclContext *DC;

    DiagnoseWalker(const DeclContext *DC, bool isExprStmt)
      : IsExprStmt(isExprStmt), Ctx(DC->getASTContext()), DC(DC) {}

    std::pair<bool, Pattern*> walkToPatternPre(Pattern *P) override {
      return { false, P };
    }

    bool walkToTypeReprPre(TypeRepr *T) override { return true; }

    bool shouldWalkCaptureInitializerExpressions() override { return true; }

    bool shouldWalkIntoTapExpression() override { return false; }

    std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
      // See through implicit conversions of the expression.  We want to be able
      // to associate the parent of this expression with the ultimate callee.
      auto Base = E;
      while (auto Conv = dyn_cast<ImplicitConversionExpr>(Base))
        Base = Conv->getSubExpr();

      if (auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
        // Verify metatype uses.
        if (isa<TypeDecl>(DRE->getDecl())) {
          if (isa<ModuleDecl>(DRE->getDecl()))
            checkUseOfModule(DRE);
          else
            checkUseOfMetaTypeName(Base);
        }

        // Verify warn_unqualified_access uses.
        checkUnqualifiedAccessUse(DRE);
        
        // Verify that special decls are eliminated.
        checkForDeclWithSpecialTypeCheckingSemantics(DRE);
        
        // Verify that `unsafeBitCast` isn't misused.
        checkForSuspiciousBitCasts(DRE, nullptr);
      }
      if (auto *MRE = dyn_cast<MemberRefExpr>(Base)) {
        if (isa<TypeDecl>(MRE->getMember().getDecl()))
          checkUseOfMetaTypeName(Base);
      }
      if (isa<TypeExpr>(Base))
        checkUseOfMetaTypeName(Base);

      if (auto *KPE = dyn_cast<KeyPathExpr>(E)) {
        // raise an error if this KeyPath contains an effectful member.
        checkForEffectfulKeyPath(KPE);
      }

      // Check function calls, looking through implicit conversions on the
      // function and inspecting the arguments directly.
      if (auto *Call = dyn_cast<ApplyExpr>(E)) {
        // Warn about surprising implicit optional promotions.
        checkOptionalPromotions(Call);
        
        // Check the callee, looking through implicit conversions.
        auto base = Call->getFn();
        unsigned uncurryLevel = 0;
        while (auto conv = dyn_cast<ImplicitConversionExpr>(base))
          base = conv->getSubExpr();

        const auto findDynamicMemberRefExpr =
            [](Expr *e) -> DynamicMemberRefExpr* {
          if (auto open = dyn_cast<OpenExistentialExpr>(e)) {
            return dyn_cast<DynamicMemberRefExpr>(open->getSubExpr());
          }
          return nullptr;
        };

        if (auto force = dyn_cast<ForceValueExpr>(base)) {
          if (auto ref = findDynamicMemberRefExpr(force->getSubExpr()))
            base = ref;
        } else if (auto bind = dyn_cast<BindOptionalExpr>(base)) {
          if (auto ref = findDynamicMemberRefExpr(bind->getSubExpr()))
            base = ref;
        }

        while (auto ignoredBase = dyn_cast<DotSyntaxBaseIgnoredExpr>(base))
          base = ignoredBase->getRHS();

        ConcreteDeclRef callee;
        if (auto *calleeDRE = dyn_cast<DeclRefExpr>(base)) {
          checkForSuspiciousBitCasts(calleeDRE, Call);
          callee = calleeDRE->getDeclRef();

        // Otherwise, try to drill down through member calls for the purposes
        // of argument-matching code below.
        } else if (auto selfApply = dyn_cast<SelfApplyExpr>(base)) {
          ++uncurryLevel;
          base = selfApply->getSemanticFn();
          if (auto calleeDRE = dyn_cast<DeclRefExpr>(base))
            callee = calleeDRE->getDeclRef();

        // Otherwise, check for a dynamic member.
        } else if (auto dynamicMRE = dyn_cast<DynamicMemberRefExpr>(base)) {
          ++uncurryLevel;
          callee = dynamicMRE->getMember();
        }

        if (callee) {
          auto *args = Call->getArgs();
          for (auto idx : indices(*args)) {
            auto *arg = args->getExpr(idx);
            checkMagicIdentifierMismatch(callee, uncurryLevel, idx, arg);

            // InOutExprs can be wrapped in some implicit casts.
            Expr *unwrapped = arg;
            if (auto *IIO = dyn_cast<InjectIntoOptionalExpr>(arg))
              unwrapped = IIO->getSubExpr();

            if (isa<InOutToPointerExpr>(unwrapped) ||
                isa<ArrayToPointerExpr>(unwrapped) ||
                isa<ErasureExpr>(unwrapped)) {
              auto operand =
                cast<ImplicitConversionExpr>(unwrapped)->getSubExpr();
              if (auto *IOE = dyn_cast<InOutExpr>(operand))
                operand = IOE->getSubExpr();

              // Also do some additional work based on how the function uses
              // the argument.
              checkConvertedPointerArgument(callee, uncurryLevel, idx,
                                            unwrapped, operand);
            }
          }
        }
      }
      
      // If we have an assignment expression, scout ahead for acceptable _'s.
      if (auto *AE = dyn_cast<AssignExpr>(E)) {
        auto destExpr = AE->getDest();
        // If the user is assigning the result of a function that returns
        // Void to _ then warn, because that is redundant.
        if (auto DAE = dyn_cast<DiscardAssignmentExpr>(destExpr)) {
          if (auto CE = dyn_cast<CallExpr>(AE->getSrc())) {
            if (isa_and_nonnull<FuncDecl>(CE->getCalledValue()) &&
                CE->getType()->isVoid()) {
              Ctx.Diags
                  .diagnose(DAE->getLoc(),
                            diag::discard_expr_void_result_redundant)
                  .fixItRemoveChars(DAE->getStartLoc(),
                                    AE->getSrc()->getStartLoc());
            }
          }
        }
      }

      // Diagnose 'self.init' or 'super.init' nested in another expression
      // or closure.
      if (auto *rebindSelfExpr = dyn_cast<RebindSelfInConstructorExpr>(E)) {
        if (!Parent.isNull() || !IsExprStmt || DC->getParent()->isLocalContext()) {
          bool isChainToSuper;
          (void)rebindSelfExpr->getCalledConstructor(isChainToSuper);
          Ctx.Diags.diagnose(E->getLoc(), diag::init_delegation_nested,
                             isChainToSuper, !IsExprStmt);
        }
      }

      // Diagnose single-element tuple expressions.
      if (auto *tupleExpr = dyn_cast<TupleExpr>(E)) {
        if (tupleExpr->getNumElements() == 1) {
          Ctx.Diags.diagnose(tupleExpr->getElementNameLoc(0),
                             diag::tuple_single_element)
            .fixItRemoveChars(tupleExpr->getElementNameLoc(0),
                              tupleExpr->getElement(0)->getStartLoc());
        }
      }

      auto diagnoseDuplicateLabels = [&](SourceLoc loc,
                                         ArrayRef<Identifier> labels) {
        llvm::SmallDenseSet<Identifier> names;
        names.reserve(labels.size());

        for (auto name : labels) {
          if (name.empty())
            continue;

          auto inserted = names.insert(name).second;
          if (!inserted) {
            Ctx.Diags.diagnose(loc, diag::tuple_duplicate_label);
            return;
          }
        }
      };

      // FIXME: Duplicate labels on enum payloads should be diagnosed
      // when declared, not when called.
      if (auto *CE = dyn_cast_or_null<CallExpr>(E)) {
        auto calledValue = CE->getCalledValue();
        if (calledValue && isa<EnumElementDecl>(calledValue)) {
          auto *args = CE->getArgs();
          SmallVector<Identifier, 4> scratch;
          diagnoseDuplicateLabels(args->getLoc(),
                                  args->getArgumentLabels(scratch));
        }
      }

      // Diagnose tuple expressions with duplicate element label.
      if (auto *tupleExpr = dyn_cast<TupleExpr>(E)) {
        diagnoseDuplicateLabels(tupleExpr->getLoc(),
                                tupleExpr->getElementNames());
      }

      // Diagnose checked casts that involve marker protocols.
      if (auto cast = dyn_cast<CheckedCastExpr>(E)) {
        checkCheckedCastExpr(cast);
      }
      
      return { true, E };
    }

    /// Visit each component of the keypath and emit a diganostic if they
    /// refer to a member that has effects.
    void checkForEffectfulKeyPath(KeyPathExpr *keyPath) {
      for (const auto &component : keyPath->getComponents()) {
        if (component.hasDeclRef()) {
          auto decl = component.getDeclRef().getDecl();
          if (auto asd = dyn_cast<AbstractStorageDecl>(decl)) {
            if (auto getter = asd->getEffectfulGetAccessor()) {
              Ctx.Diags.diagnose(component.getLoc(),
                                 diag::effectful_keypath_component,
                                 asd->getDescriptiveKind());
              Ctx.Diags.diagnose(asd->getLoc(), diag::kind_declared_here,
                                 asd->getDescriptiveKind());
            }
          }
        }
      }
    }

    void checkCheckedCastExpr(CheckedCastExpr *cast) {
      if (!isa<ConditionalCheckedCastExpr>(cast) && !isa<IsExpr>(cast))
        return;

      Type castType = cast->getCastType();
      if (!castType || !castType->isExistentialType())
        return;

      auto layout = castType->getExistentialLayout();
      for (auto proto : layout.getProtocols()) {
        if (proto->getDecl()->isMarkerProtocol()) {
          Ctx.Diags.diagnose(cast->getLoc(), diag::marker_protocol_cast,
                             proto->getDecl()->getName());
        }
      }
    }

    static Expr *lookThroughArgument(Expr *arg) {
      while (1) {
        if (auto conv = dyn_cast<ImplicitConversionExpr>(arg))
          arg = conv->getSubExpr();
        else if (auto *PE = dyn_cast<ParenExpr>(arg))
          arg = PE->getSubExpr();
        else
          break;
      }
      return arg;
    }

    void checkConvertedPointerArgument(ConcreteDeclRef callee,
                                       unsigned uncurryLevel,
                                       unsigned argIndex,
                                       Expr *pointerExpr,
                                       Expr *storage) {
      if (!isPointerIdentityArgument(callee, uncurryLevel, argIndex))
        return;

      // Flag that the argument is non-accessing.
      if (auto inout = dyn_cast<InOutToPointerExpr>(pointerExpr)) {
        inout->setNonAccessing(true);
      } else if (auto array = dyn_cast<ArrayToPointerExpr>(pointerExpr)) {
        array->setNonAccessing(true);
      }

      // TODO: warn if taking the address of 'storage' will definitely
      // yield a temporary address.
    }

    /// Is the given call argument, known to be of pointer type, just used
    /// for its pointer identity?
    bool isPointerIdentityArgument(ConcreteDeclRef ref, unsigned uncurryLevel,
                                   unsigned argIndex) {
      // FIXME: derive this from an attribute instead of hacking it based
      // on the target name!
      auto decl = ref.getDecl();

      // Assume that == and != are non-accessing uses.
      if (decl->isOperator()) {
        auto op = decl->getBaseName();
        if (op == "==" || op == "!=")
          return true;
        return false;
      }

      // NSObject.addObserver(_:forKeyPath:options:context:)
      if (uncurryLevel == 1 && argIndex == 3) {
        return decl->getName().isCompoundName("addObserver",
                                              { "", "forKeyPath",
                                                "options", "context" });
      }

      // NSObject.removeObserver(_:forKeyPath:context:)
      if (uncurryLevel == 1 && argIndex == 2) {
        return decl->getName().isCompoundName("removeObserver",
                                              { "", "forKeyPath", "context" });
      }

      return false;
    }

    /// We have a collection literal with a defaulted type, e.g. of [Any].  Emit
    /// an error if it was inferred to this type in an invalid context, which is
    /// one in which the parent expression is not itself a collection literal.
    void checkTypeDefaultedCollectionExpr(CollectionExpr *c) {
      // If the parent is a non-expression, or is not itself a literal, then
      // produce an error with a fixit to add the type as an explicit
      // annotation.
      if (c->getNumElements() == 0)
        Ctx.Diags.diagnose(c->getLoc(), diag::collection_literal_empty)
          .highlight(c->getSourceRange());
      else {
        assert(c->getType()->hasTypeRepr() &&
               "a defaulted type should always be printable");
        Ctx.Diags.diagnose(c->getLoc(), diag::collection_literal_heterogeneous,
                           c->getType())
          .highlight(c->getSourceRange())
          .fixItInsertAfter(c->getEndLoc(), " as " + c->getType()->getString());
      }
    }

    void checkMagicIdentifierMismatch(ConcreteDeclRef callee,
                                      unsigned uncurryLevel,
                                      unsigned argIndex,
                                      Expr *arg) {
      // We only care about args in the arg list.
      if (uncurryLevel != (callee.getDecl()->hasCurriedSelf() ? 1 : 0))
        return;

      // Get underlying params for both callee and caller, if declared.
      auto *calleeParam = getParameterAt(callee.getDecl(), argIndex);
      auto *callerParam = dyn_cast_or_null<ParamDecl>(
          arg->getReferencedDecl(/*stopAtParenExpr=*/true).getDecl()
      );

      // (Otherwise, we don't need to do anything.)
      if (!calleeParam || !callerParam)
        return;

      auto calleeDefaultArg = getMagicIdentifierDefaultArgKind(calleeParam);
      auto callerDefaultArg = getMagicIdentifierDefaultArgKind(callerParam);

      // If one of the parameters doesn't have a default arg, or they're both
      // compatible, everything's fine.
      if (!calleeDefaultArg || !callerDefaultArg ||
          areMagicIdentifiersCompatible(*calleeDefaultArg, *callerDefaultArg))
        return;

      StringRef calleeDefaultArgString =
          MagicIdentifierLiteralExpr::getKindString(*calleeDefaultArg);
      StringRef callerDefaultArgString =
          MagicIdentifierLiteralExpr::getKindString(*callerDefaultArg);

      // Emit main warning
      Ctx.Diags.diagnose(arg->getLoc(), diag::default_magic_identifier_mismatch,
                         callerParam->getName(), callerDefaultArgString,
                         calleeParam->getName(), calleeDefaultArgString);

      // Add "change caller default arg" fixit
      SourceLoc callerDefaultArgLoc =
          callerParam->getStructuralDefaultExpr()->getLoc();
      Ctx.Diags.diagnose(callerDefaultArgLoc,
                         diag::change_caller_default_to_match_callee,
                         callerParam->getName(), calleeDefaultArgString)
        .fixItReplace(callerDefaultArgLoc, calleeDefaultArgString);

      // Add "silence with parens" fixit
      Ctx.Diags.diagnose(arg->getLoc(),
                         diag::silence_default_magic_identifier_mismatch)
        .fixItInsert(arg->getStartLoc(), "(")
        .fixItInsertAfter(arg->getEndLoc(), ")");

      // Point to callee parameter
      Ctx.Diags.diagnose(calleeParam, diag::decl_declared_here,
                         calleeParam->getName());
    }

    Optional<MagicIdentifierLiteralExpr::Kind>
    getMagicIdentifierDefaultArgKind(const ParamDecl *param) {
      switch (param->getDefaultArgumentKind()) {
#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \
      case DefaultArgumentKind::NAME: \
        return MagicIdentifierLiteralExpr::Kind::NAME;
#include "swift/AST/MagicIdentifierKinds.def"

      case DefaultArgumentKind::None:
      case DefaultArgumentKind::Normal:
      case DefaultArgumentKind::Inherited:
      case DefaultArgumentKind::NilLiteral:
      case DefaultArgumentKind::EmptyArray:
      case DefaultArgumentKind::EmptyDictionary:
      case DefaultArgumentKind::StoredProperty:
        return None;
      }

      llvm_unreachable("Unhandled DefaultArgumentKind in "
                       "getMagicIdentifierDefaultArgKind");
    }

    static bool
    areMagicIdentifiersCompatible(MagicIdentifierLiteralExpr::Kind a,
                                  MagicIdentifierLiteralExpr::Kind b) {
      if (a == b)
        return true;

      // The rest of this handles special compatibility rules between the
      // `*SpelledAsFile` cases and various other File-related cases.
      //
      // The way we're going to do this is a bit magical. We will arrange the
      // cases in MagicIdentifierLiteralExpr::Kind so that that they sort in
      // this order:
      //
      //     #fileID < Swift 6 #file < #filePath < Swift 5 #file < others
      //
      // Before we continue, let's verify that this holds.

      using Kind = MagicIdentifierLiteralExpr::Kind;

      static_assert(Kind::FileID < Kind::FileIDSpelledAsFile,
                    "#fileID < Swift 6 #file");
      static_assert(Kind::FileIDSpelledAsFile < Kind::FilePath,
                    "Swift 6 #file < #filePath");
      static_assert(Kind::FilePath < Kind::FilePathSpelledAsFile,
                    "#filePath < Swift 5 #file");

      static_assert(Kind::FilePathSpelledAsFile < Kind::Line,
                    "Swift 5 #file < #line");
      static_assert(Kind::FilePathSpelledAsFile < Kind::Column,
                    "Swift 5 #file < #column");
      static_assert(Kind::FilePathSpelledAsFile < Kind::Function,
                    "Swift 5 #file < #function");
      static_assert(Kind::FilePathSpelledAsFile < Kind::DSOHandle,
                    "Swift 5 #file < #dsohandle");

      // The rules are all commutative, so we will take the greater of the two
      // kinds.
      auto maxKind = std::max(a, b);

      // Both Swift 6 #file and Swift 5 #file are greater than all of the cases
      // they're compatible with. So if `maxCase` is one of those two, the other
      // case must have been compatible with it!
      return maxKind == Kind::FileIDSpelledAsFile ||
             maxKind == Kind::FilePathSpelledAsFile;
    }

    void checkUseOfModule(DeclRefExpr *E) {
      // Allow module values as a part of:
      // - ignored base expressions;
      // - expressions that failed to type check.
      if (auto *ParentExpr = Parent.getAsExpr()) {
        if (isa<DotSyntaxBaseIgnoredExpr>(ParentExpr) ||
            isa<UnresolvedDotExpr>(ParentExpr))
          return;
      }

      Ctx.Diags.diagnose(E->getStartLoc(), diag::value_of_module_type);
    }

    // Diagnose metatype values that don't appear as part of a property,
    // method, or constructor reference.
    void checkUseOfMetaTypeName(Expr *E) {
      // If we've already checked this at a higher level, we're done.
      if (!AlreadyDiagnosedMetatypes.insert(E).second)
        return;

      // In Swift < 6 warn about plain type name passed as an
      // argument to a subscript, dynamic subscript, or ObjC
      // literal since it used to be accepted.
      DiagnosticBehavior behavior = DiagnosticBehavior::Error;

      if (auto *ParentExpr = Parent.getAsExpr()) {
        if (ParentExpr->isValidParentOfTypeExpr(E))
          return;

        if (!Ctx.LangOpts.isSwiftVersionAtLeast(6)) {
          if (isa<SubscriptExpr>(ParentExpr) ||
              isa<DynamicSubscriptExpr>(ParentExpr) ||
              isa<ObjectLiteralExpr>(ParentExpr)) {
            auto *argList = ParentExpr->getArgs();
            assert(argList);
            if (argList->isUnlabeledUnary())
              behavior = DiagnosticBehavior::Warning;
          }
        }
      }

      // Is this a protocol metatype?
      Ctx.Diags.diagnose(E->getStartLoc(), diag::value_of_metatype_type)
          .limitBehavior(behavior);

      // Add fix-it to insert '()', only if this is a metatype of
      // non-existential type and has any initializers.
      bool isExistential = false;
      if (auto metaTy = E->getType()->getAs<MetatypeType>()) {
        auto instanceTy = metaTy->getInstanceType();
        isExistential = instanceTy->isExistentialType();
        if (!isExistential &&
            instanceTy->mayHaveMembers() &&
            !TypeChecker::lookupMember(const_cast<DeclContext *>(DC), instanceTy,
                                       DeclNameRef::createConstructor()).empty()) {
          Ctx.Diags.diagnose(E->getEndLoc(), diag::add_parens_to_type)
            .fixItInsertAfter(E->getEndLoc(), "()");
        }
      }

      // Add fix-it to insert ".self".
      auto diag = Ctx.Diags.diagnose(E->getEndLoc(), diag::add_self_to_type);
      if (E->canAppendPostfixExpression()) {
        diag.fixItInsertAfter(E->getEndLoc(), ".self");
      } else {
        diag.fixItInsert(E->getStartLoc(), "(");
        diag.fixItInsertAfter(E->getEndLoc(), ").self");
      }
    }

    void checkUnqualifiedAccessUse(const DeclRefExpr *DRE) {
      const Decl *D = DRE->getDecl();
      if (!D->getAttrs().hasAttribute<WarnUnqualifiedAccessAttr>())
        return;

      if (auto *parentExpr = Parent.getAsExpr()) {
        if (auto *ignoredBase = dyn_cast<DotSyntaxBaseIgnoredExpr>(parentExpr)){
          if (!ignoredBase->isImplicit())
            return;
        }
        if (auto *calledBase = dyn_cast<DotSyntaxCallExpr>(parentExpr)) {
          if (!calledBase->isImplicit())
            return;
        }
      }

      const auto *VD = cast<ValueDecl>(D);
      const TypeDecl *declParent =
          VD->getDeclContext()->getSelfNominalTypeDecl();
      if (!declParent) {
        // If the declaration has been validated but not fully type-checked,
        // the attribute might be applied to something invalid.
        if (!VD->getDeclContext()->isModuleScopeContext())
          return;
        declParent = VD->getDeclContext()->getParentModule();
      }

      Ctx.Diags.diagnose(DRE->getLoc(), diag::warn_unqualified_access,
                         VD->getBaseIdentifier(),
                         VD->getDescriptiveKind(),
                         declParent->getDescriptiveKind(),
                         declParent->getName());
      Ctx.Diags.diagnose(VD, diag::decl_declared_here, VD->getName());

      if (VD->getDeclContext()->isTypeContext()) {
        Ctx.Diags.diagnose(DRE->getLoc(), diag::fix_unqualified_access_member)
          .fixItInsert(DRE->getStartLoc(), "self.");
      }

      DeclContext *topLevelSubcontext = DC->getModuleScopeContext();
      auto descriptor = UnqualifiedLookupDescriptor(
          DeclNameRef(VD->getBaseName()), topLevelSubcontext, SourceLoc());
      auto lookup = evaluateOrDefault(Ctx.evaluator,
                                      UnqualifiedLookupRequest{descriptor}, {});

      // Group results by module. Pick an arbitrary result from each module.
      llvm::SmallDenseMap<const ModuleDecl*,const ValueDecl*,4> resultsByModule;
      for (auto &result : lookup) {
        const ValueDecl *value = result.getValueDecl();
        resultsByModule.insert(std::make_pair(value->getModuleContext(),value));
      }

      // Sort by module name.
      using ModuleValuePair = std::pair<const ModuleDecl *, const ValueDecl *>;
      SmallVector<ModuleValuePair, 4> sortedResults{
        resultsByModule.begin(), resultsByModule.end()
      };
      llvm::array_pod_sort(sortedResults.begin(), sortedResults.end(),
                           [](const ModuleValuePair *lhs,
                              const ModuleValuePair *rhs) {
        return lhs->first->getName().compare(rhs->first->getName());
      });

      auto topLevelDiag = diag::fix_unqualified_access_top_level;
      if (sortedResults.size() > 1)
        topLevelDiag = diag::fix_unqualified_access_top_level_multi;

      for (const ModuleValuePair &pair : sortedResults) {
        DescriptiveDeclKind k = pair.second->getDescriptiveKind();

        SmallString<32> namePlusDot = pair.first->getName().str();
        namePlusDot.push_back('.');

        Ctx.Diags.diagnose(DRE->getLoc(), topLevelDiag,
                           namePlusDot, k, pair.first->getName())
          .fixItInsert(DRE->getStartLoc(), namePlusDot);
      }
    }
    
    void checkForDeclWithSpecialTypeCheckingSemantics(const DeclRefExpr *DRE) {
      // Referencing type(of:) and other decls with special type-checking
      // behavior as functions is not implemented. Maybe we could wrap up the
      // special-case behavior in a closure someday...
      if (TypeChecker::getDeclTypeCheckingSemantics(DRE->getDecl())
            != DeclTypeCheckingSemantics::Normal) {
        Ctx.Diags.diagnose(DRE->getLoc(), diag::unsupported_special_decl_ref,
                           DRE->getDecl()->getBaseIdentifier());
      }
    }
    
    enum BitcastableNumberKind {
      BNK_None = 0,
      BNK_Int8,
      BNK_Int16,
      BNK_Int32,
      BNK_Int64,
      BNK_Int,
      BNK_UInt8,
      BNK_UInt16,
      BNK_UInt32,
      BNK_UInt64,
      BNK_UInt,
      BNK_Float,
      BNK_Double,
    };
    BitcastableNumberKind getBitcastableNumberKind(Type t) const {
      auto decl = t->getNominalOrBoundGenericNominal();
#define MATCH_DECL(type) \
      if (decl == Ctx.get##type##Decl()) \
        return BNK_##type;
      MATCH_DECL(Int8)
      MATCH_DECL(Int16)
      MATCH_DECL(Int32)
      MATCH_DECL(Int64)
      MATCH_DECL(Int)
      MATCH_DECL(UInt8)
      MATCH_DECL(UInt16)
      MATCH_DECL(UInt32)
      MATCH_DECL(UInt64)
      MATCH_DECL(UInt)
      MATCH_DECL(Float)
      MATCH_DECL(Double)
#undef MATCH_DECL
      
      return BNK_None;
    }
    
    static constexpr unsigned BNKPair(BitcastableNumberKind a,
                                      BitcastableNumberKind b) {
      return (a << 8) | b;
    }
    
    void checkForSuspiciousBitCasts(DeclRefExpr *DRE,
                                    Expr *Parent = nullptr) {
      if (DRE->getDecl() != Ctx.getUnsafeBitCast())
        return;
      
      if (DRE->getDeclRef().getSubstitutions().empty())
        return;
      
      // Don't check the same use of unsafeBitCast twice.
      if (!AlreadyDiagnosedBitCasts.insert(DRE).second)
        return;

      auto subMap = DRE->getDeclRef().getSubstitutions();
      auto fromTy = subMap.getReplacementTypes()[0];
      auto toTy = subMap.getReplacementTypes()[1];

      // Warn about `unsafeBitCast` formulations that are undefined behavior
      // or have better-defined alternative APIs that can be used instead.
      
      // If we have a parent ApplyExpr that calls bitcast, extract the argument
      // for fixits.
      Expr *subExpr = nullptr;
      CharSourceRange removeBeforeRange, removeAfterRange;
      if (auto apply = dyn_cast_or_null<ApplyExpr>(Parent)) {
        subExpr = apply->getArgs()->getExpr(0);
        // Determine the fixit range from the start of the application to
        // the first argument, `unsafeBitCast(`
        removeBeforeRange = CharSourceRange(Ctx.SourceMgr, DRE->getLoc(),
                                            subExpr->getStartLoc());
        // Determine the fixit range from the end of the first argument to
        // the end of the application, `, to: T.self)`
        removeAfterRange = CharSourceRange(Ctx.SourceMgr,
                       Lexer::getLocForEndOfToken(Ctx.SourceMgr,
                                                  subExpr->getEndLoc()),
                       Lexer::getLocForEndOfToken(Ctx.SourceMgr,
                                                  apply->getEndLoc()));
      }
  
      // Casting to the same type or a superclass is a no-op.
      if (toTy->isEqual(fromTy) ||
          toTy->isExactSuperclassOf(fromTy)) {
        auto d = Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_is_no_op,
                                    fromTy, toTy);
        if (subExpr) {
          d.fixItRemoveChars(removeBeforeRange.getStart(),
                             removeBeforeRange.getEnd())
           .fixItRemoveChars(removeAfterRange.getStart(),
                             removeAfterRange.getEnd());
        }
        return;
      }
      
     if (auto fromFnTy = fromTy->getAs<FunctionType>()) {
        if (auto toFnTy = toTy->getAs<FunctionType>()) {
          // Casting a nonescaping function to escaping is UB.
          // `withoutActuallyEscaping` ought to be used instead.
          if (fromFnTy->isNoEscape() && !toFnTy->isNoEscape()) {
            Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_away_noescape,
                               fromTy, toTy);
          }
          // Changing function representation (say, to try to force a
          // @convention(c) function pointer to exist) is also unlikely to work.
          if (fromFnTy->getRepresentation() != toFnTy->getRepresentation()) {
            Ctx.Diags.diagnose(DRE->getLoc(),
                               diag::bitcasting_to_change_function_rep, fromTy,
                               toTy);
          }
          return;
        }
      }
      
      // Unchecked casting to a subclass is better done by unsafeDowncast.
      if (fromTy->isBindableToSuperclassOf(toTy)) {
        Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_to_downcast,
                           fromTy, toTy)
          .fixItReplace(DRE->getNameLoc().getBaseNameLoc(),
                        "unsafeDowncast");
        return;
      }

      // Casting among pointer types should use the Unsafe*Pointer APIs for
      // rebinding typed memory or accessing raw memory instead.
      PointerTypeKind fromPTK, toPTK;
      Type fromPointee = fromTy->getAnyPointerElementType(fromPTK);
      Type toPointee = toTy->getAnyPointerElementType(toPTK);
      if (fromPointee && toPointee) {
        // Casting to a pointer to the same type or UnsafeRawPointer can use
        // normal initializers on the destination type.
        if (toPointee->isEqual(fromPointee)
            || isRawPointerKind(toPTK)) {
          auto d = Ctx.Diags.diagnose(DRE->getLoc(),
                              diag::bitcasting_to_change_pointer_kind,
                              fromTy, toTy,
                              toTy->getStructOrBoundGenericStruct()->getName());
          if (subExpr) {
            StringRef before, after;
            switch (toPTK) {
            case PTK_UnsafePointer:
              // UnsafePointer(mutablePointer)
              before = "UnsafePointer(";
              after = ")";
              break;
            case PTK_UnsafeMutablePointer:
            case PTK_AutoreleasingUnsafeMutablePointer:
              before = "UnsafeMutablePointer(mutating: ";
              after = ")";
              break;
              
            case PTK_UnsafeRawPointer:
              // UnsafeRawPointer(pointer)
              before = "UnsafeRawPointer(";
              after = ")";
              break;
              
            case PTK_UnsafeMutableRawPointer:
              // UnsafeMutableRawPointer(mutating: rawPointer)
              before = fromPTK == PTK_UnsafeMutablePointer
                ? "UnsafeMutableRawPointer("
                : "UnsafeMutableRawPointer(mutating: ";
              after = ")";
              break;
            }
            d.fixItReplaceChars(removeBeforeRange.getStart(),
                                removeBeforeRange.getEnd(),
                                before)
             .fixItReplaceChars(removeAfterRange.getStart(),
                                removeAfterRange.getEnd(),
                                after);
          }
          return;
        }
        
        // Casting to a different typed pointer type should use
        // withMemoryRebound.
        if (!isRawPointerKind(fromPTK) && !isRawPointerKind(toPTK)) {
          Ctx.Diags.diagnose(DRE->getLoc(),
                             diag::bitcasting_to_change_pointee_type,
                             fromTy, toTy);
          return;
        }
        
        // Casting a raw pointer to a typed pointer should bind the memory
        // (or assume it's already bound).
        assert(isRawPointerKind(fromPTK) && !isRawPointerKind(toPTK)
               && "unhandled cast combo?!");
        Ctx.Diags.diagnose(DRE->getLoc(),
                           diag::bitcasting_to_give_type_to_raw_pointer,
                           fromTy, toTy);
        if (subExpr) {
          SmallString<64> fixitBuf;
          {
            llvm::raw_svector_ostream os(fixitBuf);
            os << ".assumingMemoryBound(to: ";
            toPointee->print(os);
            os << ".self)";
          }
          Ctx.Diags.diagnose(DRE->getLoc(),
                             diag::bitcast_assume_memory_rebound,
                             toPointee)
            .fixItRemoveChars(removeBeforeRange.getStart(),
                              removeBeforeRange.getEnd())
            .fixItReplaceChars(removeAfterRange.getStart(),
                               removeAfterRange.getEnd(),
                               fixitBuf);
          fixitBuf.clear();
          {
            llvm::raw_svector_ostream os(fixitBuf);
            os << ".bindMemory(to: ";
            toPointee->print(os);
            os << ".self, capacity: <""#capacity#"">)";
          }
          Ctx.Diags.diagnose(DRE->getLoc(),
                             diag::bitcast_bind_memory,
                             toPointee)
            .fixItRemoveChars(removeBeforeRange.getStart(),
                              removeBeforeRange.getEnd())
            .fixItReplaceChars(removeAfterRange.getStart(),
                               removeAfterRange.getEnd(),
                               fixitBuf);
        }
        return;
      }
      
      StringRef replaceBefore, replaceAfter;
      Optional<Diag<Type, Type>> diagID;
      SmallString<64> replaceBeforeBuf;

      // Bitcasting among numeric types should use `bitPattern:` initializers.
      auto fromBNK = getBitcastableNumberKind(fromTy);
      auto toBNK = getBitcastableNumberKind(toTy);
      if (fromBNK && toBNK) {
        switch (BNKPair(fromBNK, toBNK)) {
        // Combos that can be bitPattern-ed with a constructor
        case BNKPair(BNK_Int8, BNK_UInt8):
        case BNKPair(BNK_UInt8, BNK_Int8):
        case BNKPair(BNK_Int16, BNK_UInt16):
        case BNKPair(BNK_UInt16, BNK_Int16):
        case BNKPair(BNK_Int32, BNK_UInt32):
        case BNKPair(BNK_UInt32, BNK_Int32):
        case BNKPair(BNK_Int64, BNK_UInt64):
        case BNKPair(BNK_UInt64, BNK_Int64):
        case BNKPair(BNK_Int, BNK_UInt):
        case BNKPair(BNK_UInt, BNK_Int):
        case BNKPair(BNK_UInt32, BNK_Float):
        case BNKPair(BNK_UInt64, BNK_Double):
          diagID = diag::bitcasting_for_number_bit_pattern_init;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << "(bitPattern: ";
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = ")";
          break;
        
        // Combos that can be bitPattern-ed with a constructor and sign flip
        case BNKPair(BNK_Int32, BNK_Float):
        case BNKPair(BNK_Int64, BNK_Double):
          diagID = diag::bitcasting_for_number_bit_pattern_init;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << "(bitPattern: ";
            if (fromBNK == BNK_Int32)
              os << "UInt32(bitPattern: ";
            else
              os << "UInt64(bitPattern: ";
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = "))";
          break;
        
        // Combos that can be bitPattern-ed with a property
        case BNKPair(BNK_Float, BNK_UInt32):
        case BNKPair(BNK_Double, BNK_UInt64):
          diagID = diag::bitcasting_for_number_bit_pattern_property;
          replaceAfter = ".bitPattern";
          break;
        
        // Combos that can be bitPattern-ed with a property and sign flip
        case BNKPair(BNK_Float, BNK_Int32):
        case BNKPair(BNK_Double, BNK_Int64):
          diagID = diag::bitcasting_for_number_bit_pattern_property;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << "(bitPattern: ";
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = ")";
          break;

        // Combos that can be bitPattern-ed with a constructor once (U)Int is
        // converted to a sized type.
        case BNKPair(BNK_UInt, BNK_Float):
        case BNKPair(BNK_Int, BNK_UInt32):
        case BNKPair(BNK_UInt, BNK_Int32):
        case BNKPair(BNK_Int, BNK_UInt64):
        case BNKPair(BNK_UInt, BNK_Int64):
        case BNKPair(BNK_UInt, BNK_Double):
          diagID = diag::bitcasting_for_number_bit_pattern_init;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << "(bitPattern: ";
            
            if (fromBNK == BNK_Int)
              os << "Int";
            else
              os << "UInt";
            
            if (toBNK == BNK_Float
                || toBNK == BNK_Int32
                || toBNK == BNK_UInt32)
              os << "32(";
            else
              os << "64(";
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = "))";
          break;

        case BNKPair(BNK_Int, BNK_Float):
        case BNKPair(BNK_Int, BNK_Double):
          diagID = diag::bitcasting_for_number_bit_pattern_init;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << "(bitPattern: UInt";
            
            if (toBNK == BNK_Float
                || toBNK == BNK_Int32
                || toBNK == BNK_UInt32)
              os << "32(bitPattern: Int32(";
            else
              os << "64(bitPattern: Int64(";
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = ")))";
          break;
    
        // Combos that can be bitPattern-ed then converted from a sized type
        // to (U)Int.
        case BNKPair(BNK_Int32, BNK_UInt):
        case BNKPair(BNK_UInt32, BNK_Int):
        case BNKPair(BNK_Int64, BNK_UInt):
        case BNKPair(BNK_UInt64, BNK_Int):
          diagID = diag::bitcasting_for_number_bit_pattern_init;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << "(";
            if (toBNK == BNK_UInt)
              os << "UInt";
            else
              os << "Int";
            if (fromBNK == BNK_Int32 || fromBNK == BNK_UInt32)
              os << "32(bitPattern: ";
            else
              os << "64(bitPattern: ";
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = "))";
          break;
        
        case BNKPair(BNK_Float, BNK_UInt):
        case BNKPair(BNK_Double, BNK_UInt):
          diagID = diag::bitcasting_for_number_bit_pattern_property;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << "(";
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = ".bitPattern)";
          break;
          
        case BNKPair(BNK_Float, BNK_Int):
        case BNKPair(BNK_Double, BNK_Int):
          diagID = diag::bitcasting_for_number_bit_pattern_property;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << "(bitPattern: UInt(";
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = ".bitPattern))";
          break;
        
        // Combos that should be done with a value-preserving initializer.
        case BNKPair(BNK_Int, BNK_Int32):
        case BNKPair(BNK_Int, BNK_Int64):
        case BNKPair(BNK_UInt, BNK_UInt32):
        case BNKPair(BNK_UInt, BNK_UInt64):
        case BNKPair(BNK_Int32, BNK_Int):
        case BNKPair(BNK_Int64, BNK_Int):
        case BNKPair(BNK_UInt32, BNK_UInt):
        case BNKPair(BNK_UInt64, BNK_UInt):
          diagID = diag::bitcasting_to_change_from_unsized_to_sized_int;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << '(';
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = ")";
          break;
        
        default:
          // Leave other combos alone.
          break;
        }
      }
      
      // Casting a pointer to an int or back should also use bitPattern
      // initializers.
      if (fromPointee && toBNK) {
        switch (toBNK) {
        case BNK_UInt:
        case BNK_Int:
          diagID = diag::bitcasting_for_number_bit_pattern_init;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << "(bitPattern: ";
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = ")";
          break;
          
        case BNK_UInt64:
        case BNK_UInt32:
        case BNK_Int64:
        case BNK_Int32:
          diagID = diag::bitcasting_for_number_bit_pattern_init;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << '(';
            if (toBNK == BNK_UInt32 || toBNK == BNK_UInt64)
              os << "UInt(bitPattern: ";
            else
              os << "Int(bitPattern: ";
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = "))";
          break;
        
        default:
          break;
        }
      }
      if (fromBNK && toPointee) {
        switch (fromBNK) {
        case BNK_UInt:
        case BNK_Int:
          diagID = diag::bitcasting_for_number_bit_pattern_init;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << "(bitPattern: ";
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = ")";
          break;
          
        case BNK_UInt64:
        case BNK_UInt32:
        case BNK_Int64:
        case BNK_Int32:
          diagID = diag::bitcasting_for_number_bit_pattern_init;
          {
            llvm::raw_svector_ostream os(replaceBeforeBuf);
            toTy->print(os);
            os << "(bitPattern: ";
            if (fromBNK == BNK_Int32 || fromBNK == BNK_Int64)
              os << "Int(";
            else
              os << "UInt(";
          }
          replaceBefore = replaceBeforeBuf;
          replaceAfter = "))";
          break;

        default:
          break;
        }
      }
      
      if (diagID) {
        auto d = Ctx.Diags.diagnose(DRE->getLoc(), *diagID, fromTy, toTy);
        if (subExpr) {
          d.fixItReplaceChars(removeBeforeRange.getStart(),
                              removeBeforeRange.getEnd(),
                              replaceBefore);
          d.fixItReplaceChars(removeAfterRange.getStart(),
                              removeAfterRange.getEnd(),
                              replaceAfter);
        }
      }

    }
    
    /// Return true if this is a 'nil' literal.  This looks
    /// like this if the type is Optional<T>:
    ///
    ///   (dot_syntax_call_expr implicit type='Int?'
    ///     (declref_expr implicit decl=Optional.none)
    ///     (type_expr type=Int?))
    ///
    /// Or like this if it is any other ExpressibleByNilLiteral type:
    ///
    ///   (nil_literal_expr)
    ///
    bool isTypeCheckedOptionalNil(Expr *E) {
      if (dyn_cast<NilLiteralExpr>(E)) return true;

      auto CE = dyn_cast<ApplyExpr>(E->getSemanticsProvidingExpr());
      if (!CE || !CE->isImplicit())
        return false;

      // First case -- Optional.none
      if (auto DRE = dyn_cast<DeclRefExpr>(CE->getSemanticFn()))
        return DRE->getDecl() == Ctx.getOptionalNoneDecl();

      return false;
    }


    /// Warn about surprising implicit optional promotions involving operands to
    /// calls.  Specifically, we warn about these expressions when the 'x'
    /// operand is implicitly promoted to optional:
    ///
    ///       x ?? y
    ///       x == nil    // also !=
    ///
    void checkOptionalPromotions(ApplyExpr *call) {
      // We only care about binary expressions.
      auto *BE = dyn_cast<BinaryExpr>(call);
      if (!BE) return;

      // Dig out the function we're calling.
      auto fnExpr = call->getSemanticFn();
      if (auto dotSyntax = dyn_cast<DotSyntaxCallExpr>(fnExpr))
        fnExpr = dotSyntax->getSemanticFn();

      auto DRE = dyn_cast<DeclRefExpr>(fnExpr);
      if (!DRE || !DRE->getDecl()->isOperator())
        return;
      
      auto lhs = BE->getLHS();
      auto rhs = BE->getRHS();
      auto calleeName = DRE->getDecl()->getBaseName();

      Expr *subExpr = nullptr;
      if (calleeName == "??" &&
          (subExpr = isImplicitPromotionToOptional(lhs))) {
        Ctx.Diags.diagnose(DRE->getLoc(), diag::use_of_qq_on_non_optional_value,
                           subExpr->getType())
          .highlight(lhs->getSourceRange())
          .fixItRemove(SourceRange(DRE->getLoc(), rhs->getEndLoc()));
        return;
      }
      
      if (calleeName == "==" || calleeName == "!=" ||
          calleeName == "===" || calleeName == "!==") {
        if (((subExpr = isImplicitPromotionToOptional(lhs)) &&
             isTypeCheckedOptionalNil(rhs)) ||
            (isTypeCheckedOptionalNil(lhs) &&
             (subExpr = isImplicitPromotionToOptional(rhs)))) {
          bool isTrue = calleeName == "!=" || calleeName == "!==";
              
          Ctx.Diags.diagnose(DRE->getLoc(), diag::nonoptional_compare_to_nil,
                             subExpr->getType(), isTrue)
            .highlight(lhs->getSourceRange())
            .highlight(rhs->getSourceRange());
          return;
        }
      }
    }
  };

  DiagnoseWalker Walker(DC, isExprStmt);
  const_cast<Expr *>(E)->walk(Walker);

  // Diagnose uses of collection literals with defaulted types at the top
  // level.
  if (auto collection
        = dyn_cast<CollectionExpr>(E->getSemanticsProvidingExpr())) {
    if (collection->isTypeDefaulted()) {
      Walker.checkTypeDefaultedCollectionExpr(
        const_cast<CollectionExpr *>(collection));
    }
  }
}