//===--- Formatting.cpp ---------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "swift/AST/ASTWalker.h"
#include "swift/AST/GenericParamList.h"
#include "swift/AST/TypeRepr.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Frontend/Frontend.h"
#include "swift/IDE/Indenting.h"
#include "swift/IDE/SourceEntityWalker.h"
#include "swift/Parse/Lexer.h"
#include "swift/Subsystems.h"

using namespace swift;
using namespace ide;

namespace {

using StringBuilder = llvm::SmallString<64>;

static bool isOnSameLine(SourceManager &SM, SourceLoc L, SourceLoc R) {
  return Lexer::getLocForStartOfLine(SM, L) ==
    Lexer::getLocForStartOfLine(SM, R);
}

static void widenOrSet(SourceRange &First, SourceRange Second) {
  if (Second.isInvalid())
    return;
  if (First.isValid()) {
    First.widen(Second);
  } else {
    First = Second;
  }
}

/// \returns true if \c Loc is the location of the first non-comment token on
///   its line.
static bool isFirstTokenOnLine(SourceManager &SM, SourceLoc Loc) {
  assert(Loc.isValid());
  SourceLoc LineStart = Lexer::getLocForStartOfLine(SM, Loc);
  CommentRetentionMode SkipComments = CommentRetentionMode::None;
  Token First = Lexer::getTokenAtLocation(SM, LineStart, SkipComments);
  return First.getLoc() == Loc;
}

/// \returns the location of the first non-whitespace character on the line
///   containing \c Loc.
static SourceLoc
getLocForContentStartOnSameLine(SourceManager &SM, SourceLoc Loc) {
  assert(Loc.isValid());
  SourceLoc LineStart = Lexer::getLocForStartOfLine(SM, Loc);
  StringRef Indentation = Lexer::getIndentationForLine(SM, LineStart);
  return LineStart.getAdvancedLoc(Indentation.size());
}

/// \returns true if the line at \c Loc is either empty or only contains whitespace
static bool isLineAtLocEmpty(SourceManager &SM, SourceLoc Loc) {
  SourceLoc LineStart = Lexer::getLocForStartOfLine(SM, Loc);
  SourceLoc Next = Lexer::getTokenAtLocation(SM, LineStart, CommentRetentionMode::ReturnAsTokens).getLoc();
  return Next.isInvalid() || !isOnSameLine(SM, Loc, Next);
}

/// \returns the first token after the token at \c Loc.
static std::optional<Token> getTokenAfter(SourceManager &SM, SourceLoc Loc,
                                          bool SkipComments = true) {
  assert(Loc.isValid());
  CommentRetentionMode Mode = SkipComments
    ? CommentRetentionMode::None
    : CommentRetentionMode::ReturnAsTokens;
  assert(Lexer::getTokenAtLocation(SM, Loc, Mode).getLoc() == Loc);
  SourceLoc End = Lexer::getLocForEndOfToken(SM, Loc);
  Token Next = Lexer::getTokenAtLocation(SM, End, Mode);
  if (Next.getKind() == tok::NUM_TOKENS)
    return std::nullopt;
  return Next;
}

/// \returns the last token of the given kind in the open range between \c From
///   and \c To.
static std::optional<Token> getLastTokenOfKindInOpenRange(SourceManager &SM,
                                                          tok Kind,
                                                          SourceLoc From,
                                                          SourceLoc To) {
  std::optional<Token> Match;
  while (auto Next = getTokenAfter(SM, From)) {
    if (!Next || !SM.isBeforeInBuffer(Next->getLoc(), To))
      break;
    if (Next->getKind() == Kind)
      Match = Next;
    From = Next->getLoc();
  }
  return Match;
}

/// \returns true if the token at \c Loc is one of the given \c Kinds.
static bool locIsKind(SourceManager &SM, SourceLoc Loc, ArrayRef<tok> Kinds) {
  Token Tok = Lexer::getTokenAtLocation(SM, Loc);
  return Tok.getLoc() == Loc &&
      std::find(Kinds.begin(), Kinds.end(), Tok.getKind()) != Kinds.end();
}

/// \returns the given \c Loc if there is a token at that location that is one
///   of the given \c Kinds and an invalid \c SourceLocation otherwise.
static SourceLoc getLocIfKind(SourceManager &SM, SourceLoc Loc,
                              ArrayRef<tok> Kinds) {
  if (!locIsKind(SM, Loc, Kinds))
    return SourceLoc();
  return Loc;
}

/// \returns the given \c Loc if there is a token at that location that is
///   spelled with the given \c Text and an invalid \c SourceLocation otherwise.
static SourceLoc
getLocIfTokenTextMatches(SourceManager &SM, SourceLoc Loc, StringRef Text) {
  Token Tok = Lexer::getTokenAtLocation(SM, Loc);
  if (Tok.getLoc() != Loc || Tok.getKind() == tok::NUM_TOKENS ||
      Tok.getRawText() != Text)
    return SourceLoc();
  return Loc;
}

/// \returns true if the given \c VarDecl has grouping accessor braces containing
///   one or more explicit accessors.
static bool hasExplicitAccessors(VarDecl *VD) {
  auto Getter = VD->getParsedAccessor(AccessorKind::Get);
  SourceRange Braces = VD->getBracesRange();
  return Braces.isValid() && (!Getter ||
                              Getter->getAccessorKeywordLoc().isValid());
}

static ClosureExpr *findTrailingClosureFromArgument(Expr *arg) {
  if (auto TC = dyn_cast_or_null<ClosureExpr>(arg))
    return TC;
  if (auto TCL = dyn_cast_or_null<CaptureListExpr>(arg))
    return dyn_cast_or_null<ClosureExpr>(TCL->getClosureBody());
  return nullptr;
}

static size_t calcVisibleWhitespacePrefix(StringRef Line,
                                          CodeFormatOptions Options) {
  size_t Indent = 0;
  for (auto Char : Line) {
    if (Char == '\t') {
      Indent += Options.TabWidth;
    } else if (Char == ' ' || Char == '\v' || Char == '\f') {
      Indent++;
    } else {
      break;
    }
  }
  return Indent;
}

/// An indentation context of the target location
struct IndentContext {
  enum ContextKind { Exact, LineStart };

  /// The location to indent relative to.
  SourceLoc ContextLoc;

  /// Indicates whether to indent relative to the extact column of ContextLoc
  /// (Exact) or to the start of the content of the line it appears on (LineStart).
  ContextKind Kind;

  /// The number of levels to indent by.
  unsigned IndentLevel;

  IndentContext(SourceLoc Context, bool AddsIndent,
                ContextKind Kind = LineStart)
  : ContextLoc(Context), Kind(Kind), IndentLevel(AddsIndent ? 1 : 0) {
    assert(Context.isValid());
  }
};


/// A helper class used to optionally override the ContextLoc and Kind of an
/// IndentContext.
class ContextOverride {
  struct Override {
    /// The overriding ContextLoc.
    SourceLoc ContextLoc;
    /// The overriding Kind.
    IndentContext::ContextKind Kind;
    /// The location after which this override takes effect.
    SourceLoc ApplicableFrom;
  };

  /// The current override, if set.
  std::optional<Override> Value;

public:
  /// Clears this override.
  void clear() { Value = std::nullopt; }

  /// Sets this override to make an IndentContext indent relative to the exact
  /// column of AlignLoc if the IndentContext's ContextLoc is >= AlignLoc and
  /// on the same line.
  void setExact(SourceManager &SM, SourceLoc AlignLoc) {
    Value = {AlignLoc, IndentContext::Exact, AlignLoc};
  }

  /// Sets this override to propagate the given ContextLoc and Kind along to any
  /// IndentContext with a ContextLoc >= L and on the same line. If this
  /// override's existing value applies to the provided ContextLoc, its
  /// ContextLoc and Kind are propagated instead.
  ///
  /// This propagation is necessary for cases like the trailing closure of 'bar'
  /// in the example below. It's direct ContextLoc is 'bar', but we want
  /// it to be 'foo' (the ContextLoc of its parent tuple expression):
  ///
  /// \code
  /// foo(a: 1,
  ///     b: 2)(45, bar(c: 1,
  ///                   d: 2) {
  ///   fatalError()
  /// })
  /// \endcode
  SourceLoc propagateContext(SourceManager &SM, SourceLoc ContextLoc,
                             IndentContext::ContextKind Kind,
                             SourceLoc L, SourceLoc R) {
    // If the range ends on the same line as it starts, we know up front that
    // no child range can span multiple lines, so there's no need to propagate
    // ContextLoc via this override.
    if (R.isValid() && isOnSameLine(SM, L, R))
      return ContextLoc;

    // Similarly if the ContextLoc and L are on the same line, there's no need
    // to propagate. Overrides applicable to ContextLoc will already apply
    // to child ranges on the same line as L.
    if (isOnSameLine(SM, ContextLoc, L))
      return ContextLoc;

    applyIfNeeded(SM, ContextLoc, Kind);
    Value = {ContextLoc, Kind, L};
    return ContextLoc;
  }

  /// Applies the overriding ContextLoc and Kind to the given IndentContext if it
  /// starts after ApplicableFrom and on the same line.
  void applyIfNeeded(SourceManager &SM, IndentContext &Ctx) {
    // Exactly aligned indent contexts should always set a matching exact
    // alignment context override so child braces/parens/brackets are indented
    // correctly. If the given innermost indent context is Exact and the
    // override doesn't match its Kind and ContextLoc, something is wrong.
    assert((Ctx.Kind != IndentContext::Exact ||
            (Value && Value->Kind == IndentContext::Exact &&
             Value->ContextLoc == Ctx.ContextLoc)) &&
           "didn't set override ctx when exact innermost context was set?");

    applyIfNeeded(SM, Ctx.ContextLoc, Ctx.Kind);
  }

  /// Applies the overriding ContextLoc and Kind to the given Override if its
  /// ContextLoc starts after ApplicableFrom and on the same line.
  void applyIfNeeded(SourceManager &SM, SourceLoc &ContextLoc,
                     IndentContext::ContextKind &Kind) {
    if (!isApplicableTo(SM, ContextLoc))
      return;
    ContextLoc = Value->ContextLoc;
    Kind = Value->Kind;
  }

private:
  bool isApplicableTo(SourceManager &SM, SourceLoc Loc) const {
    return Value && isOnSameLine(SM, Loc, Value->ApplicableFrom) &&
        !SM.isBeforeInBuffer(Loc, Value->ApplicableFrom);
  }
};


class FormatContext {
  SourceManager &SM;
  std::optional<IndentContext> InnermostCtx;
  bool InDocCommentBlock;
  bool InCommentLine;

public:
  FormatContext(SourceManager &SM, std::optional<IndentContext> IndentCtx,
                bool InDocCommentBlock = false, bool InCommentLine = false)
      : SM(SM), InnermostCtx(IndentCtx), InDocCommentBlock(InDocCommentBlock),
        InCommentLine(InCommentLine) {}

  bool IsInDocCommentBlock() {
    return InDocCommentBlock;
  }

  bool IsInCommentLine() {
    return InCommentLine;
  }

  void padToExactColumn(StringBuilder &Builder,
                        const CodeFormatOptions &FmtOptions) {
    assert(isExact() && "Context is not exact?");
    SourceLoc AlignLoc = InnermostCtx->ContextLoc;
    CharSourceRange Range(SM, Lexer::getLocForStartOfLine(SM, AlignLoc),
                          AlignLoc);
    unsigned SpaceLength = 0;
    unsigned TabLength = 0;

    // Calculating space length
    for (auto C: Range.str())
      SpaceLength += C == '\t' ? FmtOptions.TabWidth : 1;
    SpaceLength += InnermostCtx->IndentLevel * FmtOptions.TabWidth;

    // If we're indenting past the exact column, round down to the next tab.
    if (InnermostCtx->IndentLevel)
      SpaceLength -= SpaceLength % FmtOptions.TabWidth;

    // If we are using tabs, calculating the number of tabs and spaces we need
    // to insert.
    if (FmtOptions.UseTabs) {
      TabLength = SpaceLength / FmtOptions.TabWidth;
      SpaceLength = SpaceLength % FmtOptions.TabWidth;
    }
    Builder.append(TabLength, '\t');
    Builder.append(SpaceLength, ' ');
  }

  bool isExact() {
    return InnermostCtx.has_value() &&
        InnermostCtx->Kind == IndentContext::Exact;
  }

  std::pair<unsigned, unsigned> indentLineAndColumn() {
    if (InnermostCtx)
      return SM.getLineAndColumnInBuffer(InnermostCtx->ContextLoc);
    return std::make_pair(0, 0);
  }

  bool shouldAddIndentForLine() const {
    return InnermostCtx.has_value() && InnermostCtx->IndentLevel > 0;
  }

  unsigned numIndentLevels() const {
    if (InnermostCtx)
      return InnermostCtx->IndentLevel;
    return 0;
  }
};


/// Recursively strips any trailing arguments, subscripts, generic
/// specializations, or optional bindings from the given expression.
static Expr *getContextExprOf(SourceManager &SM, Expr *E) {
  assert(E);
  if (auto *USE = dyn_cast<UnresolvedSpecializeExpr>(E)) {
    if (auto *Sub = USE->getSubExpr())
      return getContextExprOf(SM, Sub);
  } else if (auto *CE = dyn_cast<CallExpr>(E)) {
    if (auto *Fn = CE->getFn())
      return getContextExprOf(SM, Fn);
  } else if (auto *SE = dyn_cast<SubscriptExpr>(E)) {
    if (auto *B = SE->getBase())
      return getContextExprOf(SM, B);
  } else if (auto *OBE = dyn_cast<BindOptionalExpr>(E)) {
    if (auto *B = OBE->getSubExpr())
      return getContextExprOf(SM, B);
  } else if (auto *PUE = dyn_cast<PostfixUnaryExpr>(E)) {
    if (auto *B = PUE->getOperand())
      return getContextExprOf(SM, B);
  }
  return E;
}

/// Finds the ContextLoc to use for the argument of the given SubscriptExpr,
/// ApplyExpr, or UnresolvedSpecializeExpr. This is needed as the ContextLoc to
/// align their arguments with (including trailing closures) may be neither the
/// start or end of their function or base expression, as in the SubscriptExpr
/// in the example below, where 'select' is the desired ContextLoc to use.
///
/// \code
/// Base()
///   .select(x: 10
///           y: 20)[10] {
///     print($0)
///   }
///   .count
/// \endcode
static SourceLoc getContextLocForArgs(SourceManager &SM, Expr *E) {
  assert(E->getArgs() || isa<UnresolvedSpecializeExpr>(E));
  Expr *Base = getContextExprOf(SM, E);
  if (auto *UDE = dyn_cast<UnresolvedDotExpr>(Base))
    return UDE->getDotLoc();
  if (auto *UDRE = dyn_cast<UnresolvedDeclRefExpr>(Base))
    return UDRE->getLoc();
  return Base->getStartLoc();
}

/// This is a helper class intended to report every pair of matching parens,
/// braces, angle brackets, and square brackets in a given AST node, along with their ContextLoc.
class RangeWalker: protected ASTWalker {
protected:
  SourceManager &SM;

public:
  explicit RangeWalker(SourceManager &SM) : SM(SM) {}

  /// Called for every range bounded by a pair of parens, braces, square
  /// brackets, or angle brackets.
  ///
  /// \returns true to continue walking.
  virtual bool handleRange(SourceLoc L, SourceLoc R, SourceLoc ContextLoc) = 0;

  /// Called for ranges that have a separate ContextLoc but no bounding tokens.
  virtual void handleImplicitRange(SourceRange Range, SourceLoc ContextLoc) = 0;

private:
  bool handleBraces(SourceLoc L, SourceLoc R, SourceLoc ContextLoc) {
    L = getLocIfKind(SM, L, tok::l_brace);
    R = getLocIfKind(SM, R, tok::r_brace);
    return L.isInvalid() || handleRange(L, R, ContextLoc);
  }

  bool handleBraces(SourceRange Braces, SourceLoc ContextLoc) {
    return handleBraces(Braces.Start, Braces.End, ContextLoc);
  }

  bool handleParens(SourceLoc L, SourceLoc R, SourceLoc ContextLoc) {
    L = getLocIfKind(SM, L, tok::l_paren);
    R = getLocIfKind(SM, R, tok::r_paren);
    return L.isInvalid() || handleRange(L, R, ContextLoc);
  }

  bool handleSquares(SourceLoc L, SourceLoc R, SourceLoc ContextLoc) {
    L = getLocIfKind(SM, L, tok::l_square);
    R = getLocIfKind(SM, R, tok::r_square);
    return L.isInvalid() || handleRange(L, R, ContextLoc);
  }

  bool handleAngles(SourceLoc L, SourceLoc R, SourceLoc ContextLoc) {
    L = getLocIfTokenTextMatches(SM, L, "<");
    R = getLocIfTokenTextMatches(SM, R, ">");
    return L.isInvalid() || handleRange(L, R, ContextLoc);
  }

  bool handleBraceStmt(Stmt *S, SourceLoc ContextLoc) {
    if (auto *BS = dyn_cast_or_null<BraceStmt>(S))
      return handleBraces({BS->getLBraceLoc(), BS->getRBraceLoc()}, ContextLoc);
    return true;
  }

  bool walkCustomAttributes(Decl *D) {
    // CustomAttrs of non-param VarDecls are handled when this method is called
    // on their containing PatternBindingDecls (below).
    if (isa<VarDecl>(D) && !isa<ParamDecl>(D))
      return true;

    if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
      if (auto *SingleVar = PBD->getSingleVar()) {
        D = SingleVar;
      } else {
        return true;
      }
    }
    for (auto *customAttr :
         D->getParsedAttrs().getAttributes<CustomAttr, true>()) {
      if (auto *Repr = customAttr->getTypeRepr()) {
        if (!Repr->walk(*this))
          return false;
      }
      if (auto *Args = customAttr->getArgs()) {
        if (!Args->walk(*this))
          return false;
      }
    }
    return true;
  }

  MacroWalking getMacroWalkingBehavior() const override {
    return MacroWalking::Arguments;
  }

  PreWalkAction walkToDeclPre(Decl *D) override {
    if (!walkCustomAttributes(D))
      return Action::SkipNode();

    if (D->isImplicit())
      return Action::Continue();

    SourceLoc ContextLoc = D->getStartLoc();

    if (auto *GC = D->getAsGenericContext()) {
      // Asking for generic parameters on decls where they are computed, rather
      // than explicitly defined will trigger an assertion when semantic queries
      // and name lookup are disabled.
      bool SafeToAskForGenerics = !isa<ExtensionDecl>(D) &&
        !isa<ProtocolDecl>(D);
      if (SafeToAskForGenerics) {
        if (auto *GP = GC->getParsedGenericParams()) {
          if (!handleAngles(GP->getLAngleLoc(), GP->getRAngleLoc(), ContextLoc))
            return Action::Stop();
        }
      }
    }

    if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
      if (!handleBraces(NTD->getBraces(), ContextLoc))
        return Action::Stop();
    } else if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
      if (!handleBraces(ED->getBraces(), ContextLoc))
        return Action::Stop();
    } else if (auto *VD = dyn_cast<VarDecl>(D)) {
      if (!handleBraces(VD->getBracesRange(), VD->getNameLoc()))
        return Action::Stop();
    } else if (isa<AbstractFunctionDecl>(D) || isa<SubscriptDecl>(D)) {
      if (isa<SubscriptDecl>(D)) {
        if (!handleBraces(cast<SubscriptDecl>(D)->getBracesRange(), ContextLoc))
          return Action::Stop();
      }
      auto *PL = getParameterList(cast<ValueDecl>(D));
      if (!handleParens(PL->getLParenLoc(), PL->getRParenLoc(), ContextLoc))
        return Action::Stop();
    } else if (auto *PGD = dyn_cast<PrecedenceGroupDecl>(D)) {
      SourceRange Braces(PGD->getLBraceLoc(), PGD->getRBraceLoc());
      if (!handleBraces(Braces, ContextLoc))
        return Action::Stop();
    } else if (auto *PDD = dyn_cast<PoundDiagnosticDecl>(D)) {
      // TODO: add paren locations to PoundDiagnosticDecl
    }

    return Action::Continue();
  }

  PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
    if (S->isImplicit())
      return Action::Continue(S);

    if (auto *LCS = dyn_cast<LabeledConditionalStmt>(S)) {
      for (auto &Elem: LCS->getCond()) {
        if (Elem.getKind() == StmtConditionElement::CK_Availability) {
          PoundAvailableInfo *PA = Elem.getAvailability();
          if (!handleParens(PA->getLParenLoc(), PA->getRParenLoc(),
                            PA->getStartLoc()))
            return Action::Stop();
        }
      }
    }

    SourceLoc ContextLoc = S->getStartLoc();
    if (auto *BS = dyn_cast<BraceStmt>(S)) {
      if (!handleBraceStmt(BS, ContextLoc))
        return Action::Stop();
    } else if (auto *IS = dyn_cast<IfStmt>(S)) {
      if (!handleBraceStmt(IS->getThenStmt(), IS->getIfLoc()))
        return Action::Stop();
    } else if (auto *GS = dyn_cast<GuardStmt>(S)) {
      if (!handleBraceStmt(GS->getBody(), GS->getGuardLoc()))
        return Action::Stop();
    } else if (auto *FS = dyn_cast<ForEachStmt>(S)) {
      if (!handleBraceStmt(FS->getBody(), FS->getForLoc()))
        return Action::Stop();
    } else if (auto *SS = dyn_cast<SwitchStmt>(S)) {
      SourceRange Braces(SS->getLBraceLoc(), SS->getRBraceLoc());
      if (!handleBraces(Braces, SS->getSwitchLoc()))
        return Action::Stop();
    } else if (auto *DS = dyn_cast<DoStmt>(S)) {
      if (!handleBraceStmt(DS->getBody(), DS->getDoLoc()))
        return Action::Stop();
    } else if (auto *DCS = dyn_cast<DoCatchStmt>(S)) {
      if (!handleBraceStmt(DCS->getBody(), DCS->getDoLoc()))
        return Action::Stop();
    } else if (isa<CaseStmt>(S) &&
               cast<CaseStmt>(S)->getParentKind() == CaseParentKind::DoCatch) {
      auto CS = cast<CaseStmt>(S);
      if (!handleBraceStmt(CS->getBody(), CS->getLoc()))
        return Action::Stop();
    } else if (auto *RWS = dyn_cast<RepeatWhileStmt>(S)) {
      if (!handleBraceStmt(RWS->getBody(), RWS->getRepeatLoc()))
        return Action::Stop();
    } else if (auto *WS = dyn_cast<WhileStmt>(S)) {
      if (!handleBraceStmt(WS->getBody(), WS->getWhileLoc()))
        return Action::Stop();
    } else if (auto *PAS = dyn_cast<PoundAssertStmt>(S)) {
      // TODO: add paren locations to PoundAssertStmt
    }

    return Action::Continue(S);
  }

  PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
    // Walk through error expressions.
    if (auto *EE = dyn_cast<ErrorExpr>(E)) {
      if (auto *OE = EE->getOriginalExpr()) {
        llvm::SaveAndRestore<ASTWalker::ParentTy>(Parent, EE);
        OE->walk(*this);
      }
      return Action::Continue(E);
    }

    if (E->isImplicit())
      return Action::Continue(E);

    SourceLoc ContextLoc = E->getStartLoc();
    if (auto *PE = dyn_cast<ParenExpr>(E)) {
      SourceLoc L = getLocIfKind(SM, PE->getLParenLoc(),
                                 {tok::l_paren, tok::l_square});
      SourceLoc R = getLocIfKind(SM, PE->getRParenLoc(),
                                 {tok::r_paren, tok::r_square});
      if (L.isValid() && !handleRange(L, R, ContextLoc))
        return Action::Stop();
    } else if (auto *TE = dyn_cast<TupleExpr>(E)) {
      SourceLoc L = getLocIfKind(SM, TE->getLParenLoc(),
                                 {tok::l_paren, tok::l_square});
      SourceLoc R = getLocIfKind(SM, TE->getRParenLoc(),
                                 {tok::r_paren, tok::r_square});
      if (L.isValid() && !handleRange(L, R, ContextLoc))
        return Action::Stop();
    } else if (auto *CE = dyn_cast<CollectionExpr>(E)) {
      if (!handleSquares(CE->getLBracketLoc(), CE->getRBracketLoc(),
                         ContextLoc))
        return Action::Stop();
    } else if (auto *CE = dyn_cast<ClosureExpr>(E)) {
      if (!handleBraceStmt(CE->getBody(), ContextLoc))
        return Action::Stop();
      SourceRange Capture = CE->getBracketRange();
      if (!handleSquares(Capture.Start, Capture.End, Capture.Start))
        return Action::Stop();
      if (auto *PL = CE->getParameters()) {
        if (!handleParens(PL->getLParenLoc(), PL->getRParenLoc(),
                          PL->getStartLoc()))
          return Action::Stop();
      }
    } else if (auto *USE = dyn_cast<UnresolvedSpecializeExpr>(E)) {
      SourceLoc ContextLoc = getContextLocForArgs(SM, E);
      if (!handleAngles(USE->getLAngleLoc(), USE->getRAngleLoc(), ContextLoc))
        return Action::Stop();
    } else if (isa<CallExpr>(E) || isa<SubscriptExpr>(E)) {
      SourceLoc ContextLoc = getContextLocForArgs(SM, E);
      auto *Args = E->getArgs();

      auto lParenLoc = Args->getLParenLoc();
      auto rParenLoc = Args->getRParenLoc();

      if (isa<SubscriptExpr>(E)) {
        if (!handleSquares(lParenLoc, rParenLoc, ContextLoc))
          return Action::Stop();
      } else {
        if (!handleParens(lParenLoc, rParenLoc, ContextLoc))
          return Action::Stop();
      }

      if (Args->hasAnyTrailingClosures()) {
        if (auto *unaryArg = Args->getUnaryExpr()) {
          if (auto CE = findTrailingClosureFromArgument(unaryArg)) {
            if (!handleBraceStmt(CE->getBody(), ContextLoc))
              return Action::Stop();
          }
        } else {
          handleImplicitRange(Args->getOriginalArgs()->getTrailingSourceRange(),
                              ContextLoc);
        }
      }
    }
    return Action::Continue(E);
  }

  PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
    if (P->isImplicit())
      return Action::Continue(P);

    if (isa<TuplePattern>(P) || isa<ParenPattern>(P)) {
      if (!handleParens(P->getStartLoc(), P->getEndLoc(), P->getStartLoc()))
        return Action::Stop();
    }

    return Action::Continue(P);
  }

  PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
    if (auto *TT = dyn_cast<TupleTypeRepr>(T)) {
      SourceRange Parens = TT->getParens();
      if (!handleParens(Parens.Start, Parens.End, Parens.Start))
        return Action::Stop();
    } else if (isa<ArrayTypeRepr>(T) || isa<DictionaryTypeRepr>(T)) {
      if (!handleSquares(T->getStartLoc(), T->getEndLoc(), T->getStartLoc()))
        return Action::Stop();
    } else if (auto *DRTR = dyn_cast<DeclRefTypeRepr>(T)) {
      SourceLoc ContextLoc = DRTR->getNameLoc().getBaseNameLoc();
      auto Brackets = DRTR->getAngleBrackets();
      if (Brackets.isValid() &&
          !handleAngles(Brackets.Start, Brackets.End, ContextLoc))
        return Action::Stop();
    }
    return Action::Continue();
  }

  bool shouldWalkIntoGenericParams() override { return true; }
};

/// Indicates whether a range is an open or closed range.
enum class RangeKind {Closed, Open};

/// A helper class that determines whether a given node, or subrage of a node
/// should indent or not when it spans multiple lines.
class OutdentChecker: protected RangeWalker {
  SourceRange CheckRange; ///< The source range to consider.
  RangeKind CheckRangeKind; ///< Whether \c CheckRange is open or closed.
  bool IsOutdenting = false; ///< Tracks whether a seen range prevents indenting.
  llvm::DenseMap<SourceLoc, ContextOverride> LineStartToOverride;

  explicit OutdentChecker(SourceManager &SM,
                          SourceRange CheckRange,  RangeKind CheckRangeKind)
  : RangeWalker(SM), CheckRange(CheckRange), CheckRangeKind(CheckRangeKind) {
    assert(CheckRange.isValid());
  }

  void handleImplicitRange(SourceRange Range, SourceLoc ContextLoc) override {
    assert(Range.isValid() && ContextLoc.isValid());

    // Ignore ranges outside of the open/closed check range.
    if (!isInCheckRange(Range.Start, Range.End))
      return;

    propagateContextLocs(ContextLoc, Range.Start, Range.End);
  }

  bool handleRange(SourceLoc L, SourceLoc R, SourceLoc ContextLoc) override {
    assert(L.isValid() && ContextLoc.isValid());

    // Ignore parens/braces/brackets outside of the open/closed check range.
    if (!isInCheckRange(L, R))
      return true;

    // The CheckRange is made outdenting by any parens/braces/brackets with:
    // 1) a ContextLoc starts on the same line as the start of CheckRange, and
    // 2) either:
    //   a) an R token that starts its containing line, or
    //   b) an L token that isn't the ContextLoc and starts its containing line.
    //
    // E.g. for an open CheckRange covering the contents of an array literal
    // with various line bresk positions:
    //
    //   // This doesn't outdent because:
    //   [ // The array brackets are outside the open CheckRange so ignored.
    //     (1, (2, 3)), // both parens fail conditions 1 and 2.
    //     (4, (5, 6))  // both parens fail conditions 1 and 2.
    //   ]
    //
    //   // This doesn't outdent because:
    //   [(1, (2, 3)), // both parens fail condition 2.
    //      ( // these parens fail condition 1.
    //        4, (5, 6) // these parens fail conditions 1 and 2.
    //      )]
    //
    //   This outdents because:
    //   [( // These parens meet conditions 1 and 2a.
    //     1, (2, 3)
    //   ), (
    //     4, (5, 6)
    //   )]
    //
    //   This outdents because:
    //   [(1, ( // The inner parens meet conditions 1 and 2a.
    //     2, 3
    //   ), (4, (5, 6)]
    //
    //   This outdents because:
    //   [(1, (2, 3), ( // The inner parens meet conditions 1 and 2a.
    //     4, (5, 6)
    //   )]
    //
    // For a closed CheckRange covering the variable declaration below:
    //
    //   This doesn't outdent because:
    //   var x = foo(1) { // The parens and braces fail condition 2
    //     return 42 }
    //
    //   This outdents because:
    //   var x = foo(1) { // These braces meet conditions 1 and 2a.
    //      return 42
    //   }
    //
    //   This outdents because:
    //   var x = foo(1)
    //   { // These braces meet conditions 1 and 2b (their ContextLoc is 'foo').
    //      return 42
    //   }
    //
    // And for a closed CheckRange covering the call expression below:
    //
    //   This doesn't outdent because:
    //   foo(1, // These parens fail condition 2.
    //     2, 3) { return 42 } // These braces fail condition 1 and 2.
    //
    //   This outdents because:
    //   foo(1, 2, 3)
    //   { // These braces meet conditions 1 and 2b (their ContextLoc is 'foo').
    //     return 42
    //   }

    // The above conditions are not sufficient to handle cases like the below,
    // which we would like to be considered outdenting:
    //   foo(a: 1,
    //       b: 2)[x: bar { // These braces fail condition 1.
    //       return 42
    //   }]
    // To handle them, we propagate the ContextLoc of each parent range down to
    // any child ranges that start on the same line as the parent. The braces
    // above then 'inherit' the ContextLoc of their parent brackets ('foo'), and
    // pass condition 1.
    ContextLoc = propagateContextLocs(ContextLoc, L, R);

    // Ignore parens/braces/brackets that fail Condition 1.
    if (!isOnSameLine(SM, ContextLoc, CheckRange.Start))
      return true;

    // Ignore parens/braces/brackets that can't meet Condition 2.
    if (R.isValid() && isOnSameLine(SM, ContextLoc, R))
      return true;

    // Check condition 2b.
    if (ContextLoc != L && isFirstTokenOnLine(SM, L)) {
      IsOutdenting = true;
    } else if (R.isValid()) {
      // Check condition 2a.
      SourceLoc LineStart = Lexer::getLocForStartOfLine(SM, R);
      Token First = Lexer::getTokenAtLocation(SM, LineStart,
                                              CommentRetentionMode::None);
      IsOutdenting |= First.getLoc() == R;
    }

    // We only need to continue checking if it's not already outdenting.
    return !IsOutdenting;
  }

  SourceLoc propagateContextLocs(SourceLoc ContextLoc, SourceLoc L, SourceLoc R) {
    bool HasSeparateContext = !isOnSameLine(SM, L, ContextLoc);

    // Update ContextLoc for the currently active override on its line.
    ContextOverride &Upstream = getOverrideForLineContaining(ContextLoc);
    IndentContext::ContextKind Kind = IndentContext::LineStart;
    Upstream.applyIfNeeded(SM, ContextLoc, Kind);

    // If the original ContextLoc and L were on the same line, there's no need
    // to propagate anything. Child ranges later on the same line will pick up
    // whatever override we picked up above anyway, and if there wasn't
    // one, their normal ContextLoc should already be correct.
    if (!HasSeparateContext)
      return ContextLoc;

     // Set an override to propagate the context loc onto the line of L.
    ContextOverride &Downstream = getOverrideForLineContaining(L);
    ContextLoc = Downstream.propagateContext(SM, ContextLoc,
                                             Kind, L, R);
    return ContextLoc;
  }

  bool isInCheckRange(SourceLoc L, SourceLoc R) const {
    switch (CheckRangeKind) {
    case RangeKind::Open:
      return SM.isBeforeInBuffer(CheckRange.Start, L) &&
      (R.isInvalid() || SM.isBeforeInBuffer(R, CheckRange.End));
    case RangeKind::Closed:
      return !SM.isBeforeInBuffer(L, CheckRange.Start) &&
        (R.isInvalid() || !SM.isBeforeInBuffer(CheckRange.End, R));
    }
    llvm_unreachable("invalid range kind");
  }

public:
  /// Checks if a source range shouldn't indent when it crosses multiple lines.
  ///
  /// \param SM
  ///   The SourceManager managing the given source range.
  /// \param Range
  ///   The range to check.
  /// \param WalkableParent
  ///   A parent AST node that when walked covers all relevant nodes in the
  ///   given source range.
  /// \param RangeKind
  ///   Whether the given range to check is closed (the default) or open.
  template <typename T>
  static bool hasOutdent(SourceManager &SM, SourceRange Range, T *WalkableParent,
                         RangeKind RangeKind = RangeKind::Closed) {
    assert(Range.isValid());
    if (isOnSameLine(SM, Range.Start, Range.End))
      return false;
    OutdentChecker Checker(SM, Range, RangeKind);
    WalkableParent->walk(Checker);
    return Checker.IsOutdenting;
  }

  /// Checks if an AST node shouldn't indent when it crosses multiple lines.
  ///
  /// \param SM
  ///   The SourceManager managing the given source range.
  /// \param WalkableNode
  ///   The AST node to check.
  /// \param RangeKind
  ///   Whether to check the source range of \c WalkableNode as a closed (the
  ///   default) or open range.
  template <typename T>
  static bool hasOutdent(SourceManager &SM, T *WalkableNode,
                         RangeKind RangeKind = RangeKind::Closed) {
    return hasOutdent(SM, WalkableNode->getSourceRange(), WalkableNode,
                      RangeKind);
  }

private:
  ContextOverride &getOverrideForLineContaining(SourceLoc Loc) {
    SourceLoc LineStart = Lexer::getLocForStartOfLine(SM, Loc);
    auto Ret = LineStartToOverride.insert({LineStart, ContextOverride()});
    return Ret.first->getSecond();
  }
};


/// Information about an indent target that immediately follows a node being walked by
/// a \c FormatWalker instance, or optionally, that follows a trailing comma
/// after such a node.
class TrailingInfo {
  std::optional<Token> TrailingToken;
  TrailingInfo(std::optional<Token> TrailingToken)
      : TrailingToken(TrailingToken) {}

public:
  /// Whether the trailing target is on an empty line.
  bool isEmpty() const { return !TrailingToken.has_value(); }

  /// Whether the trailing target is a token with one of the given kinds.
  bool hasKind(ArrayRef<tok> Kinds) const {
    if (TrailingToken) {
      tok Kind = TrailingToken->getKind();
      return std::find(Kinds.begin(), Kinds.end(), Kind) != Kinds.end();
    }
    return false;
  }

  /// Checks if the target location immediately follows the provided \p EndLoc,
  /// optionally allowing for a single comma in between.
  static std::optional<TrailingInfo> find(SourceManager &SM, SourceLoc EndLoc,
                                          SourceLoc TargetLoc,
                                          bool LookPastTrailingComma = true) {
    // If the target is before the end of the end token, it's not trailing.
    SourceLoc TokenEndLoc = Lexer::getLocForEndOfToken(SM, EndLoc);
    if (SM.isBeforeInBuffer(TargetLoc, TokenEndLoc))
      return std::nullopt;

    // If there is no next token, the target directly trails the end token.
    auto Next = getTokenAfter(SM, EndLoc, /*SkipComments=*/false);
    if (!Next)
      return TrailingInfo{std::nullopt};

    // If the target is before or at the next token's locations, it directly
    // trails the end token.
    SourceLoc NextTokLoc = Next->getLoc();
    if (NextTokLoc == TargetLoc)
      return TrailingInfo {Next};
    if (SM.isBeforeInBuffer(TargetLoc, Next->getLoc()))
      return TrailingInfo{std::nullopt};

    // The target does not directly trail the end token. If we should look past
    // trailing commas, do so.
    if (LookPastTrailingComma && Next->getKind() == tok::comma)
      return find(SM, Next->getLoc(), TargetLoc, false);

    return std::nullopt;
  }
};


/// A helper class for aligning list elements and their bounding tokens.
class ListAligner {
  SourceManager &SM;
  SourceLoc TargetLoc; ///< The indent location.
  SourceLoc ContextLoc; ///< The owning indent context's location.
  SourceLoc IntroducerLoc; ///< The opening token before the first list element.
  SourceLoc CloseLoc; ///< The token that closes the list (optional).
  bool CloseRequired; ///< Whether a closing token is expected.
  bool AllowsTrailingSeparator; ///< Whether a final trailing comma is legal.
  bool ElementExpected; ///<Whether at least one element is expected.

  SourceLoc AlignLoc;
  SourceLoc LastEndLoc;
  bool HasOutdent = false;
  bool BreakAlignment = false;

public:

  /// Don't column-align if any element starts on the same line as IntroducerLoc
  /// but ends on a later line.
  bool BreakAlignmentIfSpanning = false;

  /// Constructs a new \c ListAligner for a list bounded by separate opening and
  /// closing tokens, e.g. tuples, array literals, parameter lists, etc.
  ///
  /// \param SM
  ///    The source manager to use.
  /// \param TargetLoc
  ///    The indent target location.
  /// \param ContextLoc
  ///    The location list items should indent relative to.
  /// \param L
  ///    The location of the token before the first item in the list, e.g. '(',
  ///    or \c case.
  /// \param R
  ///    The location of the closing token of the list, e.g. ')', if present.
  /// \param AllowsTrailingSeparator
  ///    Whether a trailing comma is legal, or indicates an incomplete list.
  ListAligner(SourceManager &SM, SourceLoc TargetLoc, SourceLoc ContextLoc,
              SourceLoc L, SourceLoc R, bool AllowsTrailingSeparator = false)
  : SM(SM), TargetLoc(TargetLoc), ContextLoc(ContextLoc),
    IntroducerLoc(L), CloseLoc(R), CloseRequired(true),
    AllowsTrailingSeparator(AllowsTrailingSeparator), ElementExpected(true) {
      assert(ContextLoc.isValid() && IntroducerLoc.isValid());
    }

  /// Constructs a new \c ListAligner for a list with only an introducing token,
  /// e.g. enum case element lists, guard/if condition pattern lists, and
  /// pattern binding declaration lists.
  ///
  /// \param SM
  ///    The source manager to use.
  /// \param TargetLoc
  ///    The indent target location.
  /// \param ContextLoc
  ///    The location list items should indent relative to.
  /// \param IntroducerLoc
  ///    The location of the token before the first item in the list, e.g. '(',
  ///    or \c case.
  /// \param ElementExpected
  ///    Whether at least one item is expected. Currently not true of 'catch'
  ///    pattern lists only.
  ListAligner(SourceManager &SM, SourceLoc TargetLoc, SourceLoc ContextLoc,
              SourceLoc IntroducerLoc, bool ElementExpected = true)
  : SM(SM), TargetLoc(TargetLoc), ContextLoc(ContextLoc),
    IntroducerLoc(IntroducerLoc), CloseLoc(SourceLoc()), CloseRequired(false),
    AllowsTrailingSeparator(false), ElementExpected(ElementExpected) {
      assert(ContextLoc.isValid() && IntroducerLoc.isValid());
    }

  /// Update the alignment for a list element.
  ///
  /// \param Start
  ///   The start location of the element.
  /// \param End
  ///   The end location of the element
  /// \param WalkableParent
  ///   An AST node that is, or contains the element, and is walkable.
  template <typename T>
  void updateAlignment(SourceLoc Start, SourceLoc End, T *WalkableParent) {
    updateAlignment(SourceRange(Start, End), WalkableParent);
  }

  /// Update the alignment for a list element.
  ///
  /// \param Range
  ///   The source range of the element.
  /// \param WalkableParent
  ///   An AST node that is, or contains the element, and is walkable.
  template <typename T>
  void updateAlignment(SourceRange Range, T *WalkableParent) {
    assert(Range.isValid());
    LastEndLoc = Range.End;

    if (isOnSameLine(SM, IntroducerLoc, Range.Start)) {
      HasOutdent |= OutdentChecker::hasOutdent(SM, Range, WalkableParent);
      if (BreakAlignmentIfSpanning)
        BreakAlignment |= !isOnSameLine(SM, IntroducerLoc, Range.End);
    }

    if (HasOutdent || !SM.isBeforeInBuffer(Range.Start, TargetLoc))
      return;
    if (AlignLoc.isValid()) {
      if (isOnSameLine(SM, Range.Start, AlignLoc) ||
          !isFirstTokenOnLine(SM, Range.Start))
        return;
      AlignLoc = getLocForContentStartOnSameLine(SM, Range.Start);
    } else if (isOnSameLine(SM, IntroducerLoc, Range.Start)) {
      AlignLoc = Range.Start;
    }
  }

  /// Returns the list's IndentContext (if applicable to the TargetLoc) and sets
  /// an exact alignment override if needed.
  ///
  /// \note This should only be called after calling \c updateAlignment on every
  ///   element range.
  /// \param Override
  ///   A ContextOverride object to set
  std::optional<IndentContext>
  getContextAndSetAlignment(ContextOverride &Override) {
    // If the target is before the introducer token, or on it and it is also
    // the context loc, the list shouldn't be an indent context.
    if (SM.isBeforeInBuffer(TargetLoc, IntroducerLoc))
      return std::nullopt;
    if (TargetLoc == IntroducerLoc && ContextLoc == IntroducerLoc)
      return std::nullopt;

    // Get the end location of the (possibly incomplete) list.
    bool HasTrailingComma = false;
    SourceLoc End = getEffectiveEndLoc(HasTrailingComma);
    assert(End.isValid());

    // If the target is past the end of the list we may still be an indent
    // context.
    bool TargetIsTrailing = false;
    if (!SM.isBeforeInBuffer(TargetLoc, Lexer::getLocForEndOfToken(SM, End))) {
      // If the close token is present, we're not.
      if (CloseLoc.isValid())
        return std::nullopt;

      // If there's no trailing comma and a close token isn't required, we're
      // only a context if there no elements yet but at least one is expected,
      // e.g. in an if condition list.
      if (!HasTrailingComma && !CloseRequired &&
          (LastEndLoc.isValid() || !ElementExpected))
        return std::nullopt;

      // If the target isn't immediately trailing the end loc, we're not.
      if (!TrailingInfo::find(SM, End, TargetLoc,
                          /*LookPastTrailingComma=*/!HasTrailingComma)) {
        return std::nullopt;
      }
      TargetIsTrailing = true;
    }

    bool ShouldIndent = shouldIndent(HasTrailingComma, TargetIsTrailing);
    if (ShouldIndent && !BreakAlignment && AlignLoc.isValid()) {
      setAlignmentIfNeeded(Override);
      return IndentContext {AlignLoc, false, IndentContext::Exact};
    }
    return IndentContext {ContextLoc, ShouldIndent};
  }

  /// Sets an exact alignment override for child indent contexts, if needed.
  ///
  /// This should be called before returning an \c IndentContext for a subrange
  /// of the list.
  void setAlignmentIfNeeded(ContextOverride &Override) {
    if (HasOutdent || BreakAlignment || AlignLoc.isInvalid())
      return;
    Override.setExact(SM, AlignLoc);
  }

private:
  bool shouldIndent(bool HasTrailingComma, bool TargetIsTrailing) const {
    if (HasOutdent || TargetLoc == IntroducerLoc)
      return false;
    if (TargetLoc == CloseLoc)
      return !AllowsTrailingSeparator && HasTrailingComma;
    if (TargetIsTrailing) {
      return  CloseLoc.isInvalid() &&
        ((LastEndLoc.isInvalid() && ElementExpected) ||
         HasTrailingComma);
    }
    return true;
  }

  SourceLoc getEffectiveEndLoc(bool &HasTrailingComma) const {
    if (LastEndLoc.isInvalid())
      return CloseLoc.isValid() ? CloseLoc : IntroducerLoc;

    SourceLoc EffectiveEnd = LastEndLoc;
    if (locIsKind(SM, LastEndLoc, tok::comma)) {
      HasTrailingComma = true;
    } else {
      std::optional<Token> AfterLast = getTokenAfter(SM, LastEndLoc);
      if (AfterLast && AfterLast->is(tok::comma)) {
        HasTrailingComma = true;
        EffectiveEnd = AfterLast->getLoc();
      }
    }

    if (CloseLoc.isValid())
      return CloseLoc;
    return EffectiveEnd;
  }
};


/// Walks an AST Node to determine the \c FormatContext of the target indent location.
///
/// It only walks into nodes whose source range overlaps, or immediately
/// precedes the target indent location.
class FormatWalker : public ASTWalker {
  SourceFile &SF;
  SourceManager &SM;
  CodeFormatOptions &FmtOptions;
  ArrayRef<Token> TokenList;

  SourceLoc TargetLocation;
  SourceLoc TargetLineLoc;
  llvm::SmallPtrSet<void *, 16> NodesToSkip;
  ArrayRef<Token>::iterator CurrentTokIt;

  /// The innermost indent context of the target location.
  std::optional<IndentContext> InnermostCtx;
  /// A conditionally applicable indent context override.
  ContextOverride CtxOverride;
  /// Whether the target location appears within a doc comment block.
  bool InDocCommentBlock = false;
  /// Whether the target location appears within a line comment.
  bool InCommentLine = false;

  /// The range of the string literal the target is inside of (if any, invalid otherwise).
  CharSourceRange StringLiteralRange;

public:
  explicit FormatWalker(SourceFile &SF, SourceManager &SM, CodeFormatOptions &Options)
  : SF(SF), SM(SM), FmtOptions(Options), TokenList(SF.getAllTokens()),
    CurrentTokIt(TokenList.begin()) {}

  /// Compute the \c FormatContext of the given source location.
  ///
  /// \note The given location should point to the content start of its line.
  FormatContext walkToLocation(SourceLoc Loc) {
    InnermostCtx = std::nullopt;
    CtxOverride.clear();
    TargetLocation = Loc;
    TargetLineLoc = Lexer::getLocForStartOfLine(SM, TargetLocation);
    InDocCommentBlock = InCommentLine = false;
    StringLiteralRange = CharSourceRange();
    NodesToSkip.clear();
    CurrentTokIt = TokenList.begin();

    SF.walk(*this);
    scanTokensUntil(SourceLoc());

    if (InnermostCtx)
      CtxOverride.applyIfNeeded(SM, *InnermostCtx);

    if (StringLiteralRange.isValid()) {
      assert(!InDocCommentBlock && !InCommentLine &&
             "Target is in both a string and comment");
      InnermostCtx = indentWithinStringLiteral();
    } else {
      assert(!InDocCommentBlock || !InCommentLine &&
             "Target is in both a doc comment block and comment line");
    }

    return FormatContext(SM, InnermostCtx, InDocCommentBlock,
                         InCommentLine);
  }

private:
  std::optional<IndentContext> indentWithinStringLiteral() {
    assert(StringLiteralRange.isValid() && "Target is not within a string literal");

    // This isn't ideal since if the user types """""" and then an enter
    // inside after, we won't indent the end quotes. But indenting the end
    // quotes could lead to an error in the rest of the string, so best to
    // avoid it entirely for now.
    if (isOnSameLine(SM, TargetLineLoc, StringLiteralRange.getEnd()))
      return IndentContext {TargetLocation, false, IndentContext::Exact};

    // If there's contents before the end quotes then it's likely the quotes
    // are actually the start quotes of the next string in the file. Pretend
    // they don't exist so their indent doesn't affect the indenting.
    SourceLoc EndLineContentLoc =
        getLocForContentStartOnSameLine(SM, StringLiteralRange.getEnd());
    bool HaveEndQuotes = CharSourceRange(SM, EndLineContentLoc,
                                         StringLiteralRange.getEnd())
        .str() == "\"\"\"";

    if (!HaveEndQuotes) {
      // Indent to the same indentation level as the first non-empty line
      // before the target. If that line is the start line then either use
      // the same indentation of the start quotes if they are on their own
      // line, or an extra indentation otherwise.
      //
      // This will indent lines with content on it as well, which should be
      // fine since it is quite unlikely anyone would format a range that
      // includes an unterminated string.

      SourceLoc AlignLoc = TargetLineLoc;
      while (SM.isBeforeInBuffer(StringLiteralRange.getStart(), AlignLoc)) {
        AlignLoc = Lexer::getLocForStartOfLine(SM, AlignLoc.getAdvancedLoc(-1));
        if (!isLineAtLocEmpty(SM, AlignLoc)) {
          AlignLoc = getLocForContentStartOnSameLine(SM, AlignLoc);
          break;
        }
      }

      if (isOnSameLine(SM, AlignLoc, StringLiteralRange.getStart())) {
        SourceLoc StartLineContentLoc =
            getLocForContentStartOnSameLine(SM, StringLiteralRange.getStart());
        bool StartLineOnlyQuotes = CharSourceRange(SM, StartLineContentLoc,
                                                   StringLiteralRange.getEnd())
            .str().starts_with(StringRef("\"\"\""));
        if (!StartLineOnlyQuotes)
          return IndentContext {StringLiteralRange.getStart(), true};

        AlignLoc = StringLiteralRange.getStart();
      }

      return IndentContext {AlignLoc, false, IndentContext::Exact};
    }

    // If there are end quotes, only enforce a minimum indentation. We don't
    // want to add any other indentation since that could add unintended
    // whitespace to existing strings. Could change this if the full range
    // was passed rather than a single line - in that case we *would* indent
    // if the range was a single empty line.

    CharSourceRange TargetIndentRange =
        CharSourceRange(SM, Lexer::getLocForStartOfLine(SM, TargetLocation),
                        TargetLocation);
    CharSourceRange EndIndentRange =
        CharSourceRange(SM, Lexer::getLocForStartOfLine(SM, EndLineContentLoc),
                        EndLineContentLoc);

    size_t TargetIndent =
        calcVisibleWhitespacePrefix(TargetIndentRange.str(), FmtOptions);
    size_t EndIndent =
        calcVisibleWhitespacePrefix(EndIndentRange.str(), FmtOptions);
    if (TargetIndent >= EndIndent)
      return IndentContext {TargetLocation, false, IndentContext::Exact};

    return IndentContext {EndLineContentLoc, false, IndentContext::Exact};
  }

#pragma mark ASTWalker overrides and helpers

  bool walkCustomAttributes(Decl *D) {
    // CustomAttrs of non-param VarDecls are handled when this method is called
    // on their containing PatternBindingDecls (below).
    if (isa<VarDecl>(D) && !isa<ParamDecl>(D))
      return true;

    if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
      if (auto *SingleVar = PBD->getSingleVar()) {
        D = SingleVar;
      } else {
        return true;
      }
    }
    for (auto *customAttr :
         D->getParsedAttrs().getAttributes<CustomAttr, true>()) {
      if (auto *Repr = customAttr->getTypeRepr()) {
        if (!Repr->walk(*this))
          return false;
      }
      if (auto *Args = customAttr->getArgs()) {
        if (!Args->walk(*this))
          return false;
      }
    }
    return true;
  }

  MacroWalking getMacroWalkingBehavior() const override {
    return MacroWalking::Arguments;
  }

  PreWalkAction walkToDeclPre(Decl *D) override {
    if (!walkCustomAttributes(D))
      return Action::SkipNode();

    auto Action = HandlePre(D, D->isImplicit());
    if (Action.shouldGenerateIndentContext()) {
      if (auto IndentCtx = getIndentContextFrom(D, Action.Trailing))
        InnermostCtx = IndentCtx;
    }

    // Walk accessors via their pattern binding decl. They aren't walked via
    // their VarDecls due to their non-overlapping range, so they'd be skipped
    // otherwise.
    if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
      if (Action.shouldVisitChildren()) {
        for (auto I: range(PBD->getNumPatternEntries())) {
          auto *P = PBD->getPattern(I);
          if (!P)
            continue;
          bool Cancelled = false;
          P->forEachVariable([&](VarDecl *VD) {
            if (Cancelled || VD->getBracesRange().isInvalid())
              return;
            for (auto *AD : VD->getAllAccessors()) {
              if (AD->walk(*this)) {
                Cancelled = true;
                break;
              }
            }
          });
        }
      }
    }

    // FIXME: We ought to be able to use Action::VisitChildrenIf here, but we'd
    // need to ensure the AST is walked in source order (currently not the case
    // for things like postfix operators).
    return Action::VisitNodeIf(Action.shouldVisitChildren());
  }

  PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
    auto Action = HandlePre(S, S->isImplicit());
    if (Action.shouldGenerateIndentContext()) {
      if (auto IndentCtx = getIndentContextFrom(S, Action.Trailing))
        InnermostCtx = IndentCtx;
    }
    // FIXME: We ought to be able to use Action::VisitChildrenIf here, but we'd
    // need to ensure the AST is walked in source order (currently not the case
    // for things like postfix operators).
    return Action::VisitNodeIf(Action.shouldVisitChildren(), S);
  }

  PreWalkResult<ArgumentList *>
  walkToArgumentListPre(ArgumentList *Args) override {
    SourceLoc ContextLoc;
    if (auto *E = Parent.getAsExpr()) {
      // Retrieve the context loc from the parent call. Note that we don't
      // perform this if we're walking the ArgumentList directly for e.g an
      // interpolated string literal.
      if (E->getArgs() == Args)
        ContextLoc = getContextLocForArgs(SM, E);
    } else if (auto *D = Parent.getAsDecl()) {
      if (auto *MED = dyn_cast<MacroExpansionDecl>(D)) {
        if (MED->getArgs() == Args) {
          ContextLoc = MED->getStartLoc();
        }
      }
    }

    auto Action = HandlePre(Args, Args->isImplicit());
    if (Action.shouldGenerateIndentContext()) {
      if (auto Ctx = getIndentContextFrom(Args, Action.Trailing, ContextLoc))
        InnermostCtx = Ctx;
    }
    // FIXME: We ought to be able to use Action::VisitChildrenIf here, but we'd
    // need to ensure the AST is walked in source order (currently not the case
    // for things like postfix operators).
    return Action::VisitNodeIf(Action.shouldVisitChildren(), Args);
  }

  PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
    if (E->getKind() == ExprKind::StringLiteral &&
        SM.isBeforeInBuffer(E->getStartLoc(), TargetLocation) &&
        SM.isBeforeInBuffer(TargetLocation,
                            Lexer::getLocForEndOfToken(SM, E->getEndLoc()))) {
      StringLiteralRange = Lexer::getCharSourceRangeFromSourceRange(SM, E->getSourceRange());
    }

    // Create a default indent context for all top-level expressions
    if (isStatementListItem()) {
      SourceRange Range = E->getSourceRange();
      if (Range.isValid() && isTargetContext(Range)) {
        InnermostCtx = IndentContext {
          E->getStartLoc(),
          !OutdentChecker::hasOutdent(SM, E)
        };
      }
    }

    auto Action = HandlePre(E, E->isImplicit());
    if (Action.shouldGenerateIndentContext()) {
      if (auto IndentCtx = getIndentContextFrom(E, Action.Trailing))
        InnermostCtx = IndentCtx;
    }

    // Don't visit the child expressions of interpolated strings directly -
    // visit only the argument of each appendInterpolation call instead, and
    // set StringLiteralRange if needed for each segment.
    if (auto *ISL = dyn_cast<InterpolatedStringLiteralExpr>(E)) {
      if (Action.shouldVisitChildren()) {
        llvm::SaveAndRestore<ASTWalker::ParentTy>(Parent, ISL);
        SourceLoc PrevStringStart = ISL->getStartLoc();
        ISL->forEachSegment(SF.getASTContext(),
                            [&](bool IsInterpolation, CallExpr *CE) {
          if (IsInterpolation) {
            // Handle the preceeding string segment.
            CharSourceRange StringRange(SM, PrevStringStart, CE->getStartLoc());
            if (StringRange.contains(TargetLocation)) {
              StringLiteralRange =
                  Lexer::getCharSourceRangeFromSourceRange(SM, E->getSourceRange());
              return;
            }
            // Walk into the interpolation segment.
            CE->getArgs()->walk(*this);
          } else {
            PrevStringStart = CE->getStartLoc();
          }
        });
        // Handle the trailing string segment.
        SourceLoc End = Lexer::getLocForEndOfToken(SM, ISL->getStartLoc());
        CharSourceRange StringRange(SM, PrevStringStart, End);
        if (StringRange.contains(TargetLocation))
          StringLiteralRange =
              Lexer::getCharSourceRangeFromSourceRange(SM, E->getSourceRange());

        return Action::SkipNode(E);
      }
    }

    // Walk through error expressions.
    if (auto *EE = dyn_cast<ErrorExpr>(E)) {
      if (Action.shouldVisitChildren()) {
        if (auto *OE = EE->getOriginalExpr()) {
          llvm::SaveAndRestore<ASTWalker::ParentTy>(Parent, EE);
          OE->walk(*this);
        }
        return Action::SkipNode(E);
      }
    }

    // FIXME: We ought to be able to use Action::VisitChildrenIf here, but we'd
    // need to ensure the AST is walked in source order (currently not the case
    // for things like postfix operators).
    return Action::VisitNodeIf(Action.shouldVisitChildren(), E);
  }

  PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
    auto Action = HandlePre(P, P->isImplicit());
    if (Action.shouldGenerateIndentContext()) {
      if (auto IndentCtx = getIndentContextFrom(P, Action.Trailing))
        InnermostCtx = IndentCtx;
    }
    // FIXME: We ought to be able to use Action::VisitChildrenIf here, but we'd
    // need to ensure the AST is walked in source order (currently not the case
    // for things like postfix operators).
    return Action::VisitNodeIf(Action.shouldVisitChildren(), P);
  }

  PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
    auto Action = HandlePre(T, false);
    if (Action.shouldGenerateIndentContext()) {
      if (auto IndentCtx = getIndentContextFrom(T, Action.Trailing))
        InnermostCtx = IndentCtx;
    }
    // FIXME: We ought to be able to use Action::VisitChildrenIf here, but we'd
    // need to ensure the AST is walked in source order (currently not the case
    // for things like postfix operators).
    return Action::VisitNodeIf(Action.shouldVisitChildren());
  }

  PostWalkAction walkToDeclPost(Decl *D) override {
    return HandlePost(D).Action;
  }
  PostWalkAction walkToTypeReprPost(TypeRepr *T) override {
    return HandlePost(T).Action;
  }

  PostWalkResult<Stmt *> walkToStmtPost(Stmt *S) override {
    return HandlePost(S);
  }

  PostWalkResult<Expr *> walkToExprPost(Expr *E) override {
    return HandlePost(E);
  }

  PostWalkResult<Pattern *> walkToPatternPost(Pattern *P) override {
    return HandlePost(P);
  }

  bool shouldWalkIntoGenericParams() override { return true; }


#pragma mark Visitation helpers

  struct VisitAction {
    enum : unsigned { Skip, VisitChildren, GetContext } action;
    std::optional<TrailingInfo> Trailing;

    bool shouldVisitChildren() const { return action >= VisitChildren; }
    bool shouldGenerateIndentContext() const { return action >= GetContext; }
  };

  template <class T>
  VisitAction HandlePre(T* Node, bool IsImplicit) {
    SourceLoc Start = Node->getStartLoc(), End = Node->getEndLoc();

    if (Start.isInvalid())
      return {VisitAction::VisitChildren, std::nullopt};

    std::optional<TrailingInfo> Trailing =
        TrailingInfo::find(SM, End, TargetLocation);
    scanTokensUntil(Start);

    if (!isTargetContext(Start, End) && !Trailing)
      return {VisitAction::Skip, std::nullopt};
    if (!NodesToSkip.count(Node) && !IsImplicit)
      return {VisitAction::GetContext, Trailing};
    return {VisitAction::VisitChildren, Trailing};
  }

  template <typename T>
  PostWalkResult<T *> HandlePost(T *Node) {
    if (SM.isBeforeInBuffer(TargetLocation, Node->getStartLoc()))
      return Action::Stop();

    return Action::Continue(Node);
  }

  void scanTokensUntil(SourceLoc Loc) {
    if (InDocCommentBlock || InCommentLine || StringLiteralRange.isValid())
      return;
    for (auto Invalid = Loc.isInvalid(); CurrentTokIt != TokenList.end() &&
         (Invalid || SM.isBeforeInBuffer(CurrentTokIt->getLoc(), Loc));
         ++CurrentTokIt) {
      if (CurrentTokIt->getKind() == tok::comment) {
        CharSourceRange CommentRange = CurrentTokIt->getRange();
        SourceLoc StartLineLoc = Lexer::getLocForStartOfLine(
            SM, CommentRange.getStart());

        SourceLoc EndLineLoc = Lexer::getLocForStartOfLine(
            SM, CommentRange.getEnd());
        auto TokenStr = CurrentTokIt->getRange().str();
        InDocCommentBlock |= SM.isBeforeInBuffer(StartLineLoc, TargetLineLoc) && !SM.isBeforeInBuffer(EndLineLoc, TargetLineLoc) &&
            TokenStr.starts_with("/*");
        InCommentLine |= StartLineLoc == TargetLineLoc &&
            TokenStr.starts_with("//");
      } else if (CurrentTokIt->getKind() == tok::unknown &&
                 CurrentTokIt->getRange().str().starts_with("\"\"\"")) {
        StringLiteralRange = CurrentTokIt->getRange();
      }
    }
  }

  /// When visiting an expression, returns true if it's a stement level
  /// expression.
  bool isStatementListItem() {
    if (auto *S = Parent.getAsStmt()) {
      if (auto *RS = dyn_cast<ReturnStmt>(S))
        return RS->isImplicit();
      return isa<BraceStmt>(S);
    }
    if (auto *E = Parent.getAsExpr()) {
      return isa<ClosureExpr>(E);
    }
    return false;
  }

  /// Checks whether the given range is an indent context of the target location.
  ///
  /// \return \c Start < \c TargetLocation <= \c End.
  bool isTargetContext(SourceLoc Start, SourceLoc End) const {
    assert(Start.isValid());
    // Start < Target <= End
    return SM.isBeforeInBuffer(Start, TargetLocation) &&
        (End.isInvalid() ||
         SM.isBeforeInBuffer(TargetLocation,
                             Lexer::getLocForEndOfToken(SM, End)));
  }

  /// Checks whether the given range is an indent context of the target location.
  ///
  /// \return \c Range.Start < \c TargetLocation <= \c Range.End.
  bool isTargetContext(SourceRange Range) const {
    return isTargetContext(Range.Start, Range.End);
  }

  /// Checks whether the given range overlaps the target location.
  ///
  /// \return \c Start <= \c TargetLocation <= \c End
  bool overlapsTarget(SourceLoc Start, SourceLoc End) const {
    assert(Start.isValid());
    return !SM.isBeforeInBuffer(TargetLocation, Start) &&
      (End.isInvalid() ||
       SM.isBeforeInBuffer(TargetLocation,
                           Lexer::getLocForEndOfToken(SM, End)));
  }

  /// Checks whether the given range overlaps the target location.
  ///
  /// \return \c Range.Start <= \c TargetLocation <= \c Range.End
  bool overlapsTarget(SourceRange Range) const {
    assert(Range.isValid());
    return overlapsTarget(Range.Start, Range.End);
  }

  /// Checks whether the given range contains the target location.
  ///
  /// \return \c Start < \c TargetLocation < \c End
  bool containsTarget(SourceLoc Start, SourceLoc End) const {
    assert(Start.isValid());
    return SM.isBeforeInBuffer(Start, TargetLocation) &&
      (End.isInvalid() || SM.isBeforeInBuffer(TargetLocation, End));
  }

  /// Checks whether the given range contains the target location.
  ///
  /// \return \c Range.Start < \c TargetLocation < \c Range.End
  bool containsTarget(SourceRange Range) const {
    assert(Range.isValid());
    return containsTarget(Range.Start, Range.End);
  }

#pragma mark Declaration indent contexts

  std::optional<IndentContext>
  getIndentContextFrom(Decl *D, std::optional<TrailingInfo> TrailingTarget) {

    if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
      SourceLoc ContextLoc = AFD->getStartLoc();
      // If this is a getter without a 'get' loc, the context loc is the start
      // of its storage.
      if (auto *AD = dyn_cast<AccessorDecl>(AFD)) {
        if (AD->isGetter() && AD->getAccessorKeywordLoc().isInvalid()) {
          auto *ASD = AD->getStorage();
          if (auto *VD = dyn_cast_or_null<VarDecl>(ASD)) {
            ContextLoc = VD->getStartLoc();
          } else if (auto *SD = dyn_cast_or_null<SubscriptDecl>(ASD)) {
            ContextLoc = SD->getStartLoc();
          }
        }
      }
      if (auto Ctx = getIndentContextFrom(AFD->getBody(), ContextLoc))
        return Ctx;
      if (auto Ctx = getIndentContextFrom(AFD->getParameters(), ContextLoc))
        return Ctx;
      if (auto Ctx = getIndentContextFrom(AFD->getParsedGenericParams(), ContextLoc, D))
        return Ctx;
      if (auto Ctx = getIndentContextFrom(AFD->getTrailingWhereClause(), ContextLoc, D))
        return Ctx;

      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {ContextLoc, false};
    }

    if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
      SourceLoc ContextLoc = NTD->getStartLoc();

      if (auto Ctx = getIndentContextFromInherits(NTD->getInherited(), ContextLoc))
        return Ctx;
      if (auto Ctx = getIndentContextFromBraces(NTD->getBraces(), ContextLoc, NTD))
        return Ctx;
      if (auto Ctx = getIndentContextFrom(NTD->getParsedGenericParams(), ContextLoc, D))
        return Ctx;
      if (auto Ctx = getIndentContextFrom(NTD->getTrailingWhereClause(), ContextLoc, D))
        return Ctx;

      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {ContextLoc, false};
    }

    if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
      SourceLoc ContextLoc = ED->getStartLoc();

      if (auto Ctx = getIndentContextFromInherits(ED->getInherited(), ContextLoc))
        return Ctx;
      if (auto Ctx = getIndentContextFromBraces(ED->getBraces(), ContextLoc, ED))
        return Ctx;
      if (auto Ctx = getIndentContextFrom(ED->getTrailingWhereClause(), ContextLoc, D))
        return Ctx;

      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {ContextLoc, false};
    }

    if (auto *ECD = dyn_cast<EnumCaseDecl>(D)) {
      SourceLoc CaseLoc = ECD->getLoc();
      ListAligner Aligner(SM, TargetLocation, CaseLoc, CaseLoc);
      for (auto *Elem: ECD->getElements()) {
        if (Elem->isImplicit())
          continue;
        SourceRange ElemRange = Elem->getSourceRange();
        Aligner.updateAlignment(ElemRange, Elem);
      }
      return Aligner.getContextAndSetAlignment(CtxOverride);
    }

    if (auto *EED = dyn_cast<EnumElementDecl>(D)) {
      SourceLoc ContextLoc = EED->getStartLoc();
      if (auto Ctx = getIndentContextFrom(EED->getParameterList()))
        return Ctx;

      if (TrailingTarget)
        return std::nullopt;

      return IndentContext {
        ContextLoc,
        !OutdentChecker::hasOutdent(SM, EED)
      };
    }

    if (auto *SD = dyn_cast<SubscriptDecl>(D)) {
      SourceLoc ContextLoc = SD->getStartLoc();

      if (auto Ctx = getIndentContextFromBraces(SD->getBracesRange(), ContextLoc, SD))
        return Ctx;
      if (auto Ctx = getIndentContextFrom(SD->getIndices(), ContextLoc))
        return Ctx;
      if (auto Ctx = getIndentContextFrom(SD->getParsedGenericParams(), ContextLoc, D))
        return Ctx;
      if (auto Ctx = getIndentContextFrom(SD->getTrailingWhereClause(), ContextLoc, D))
        return Ctx;

      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {ContextLoc, false};
    }

    if (auto *PGD = dyn_cast<PrecedenceGroupDecl>(D)) {
      SourceLoc ContextLoc = PGD->getStartLoc();
      SourceLoc L = PGD->getLBraceLoc(), R = PGD->getRBraceLoc();

      if (auto Ctx = getIndentContextFromBraces(L, R, ContextLoc, PGD))
        return Ctx;

      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {PGD->getStartLoc(), false};
    }

    if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
      SourceLoc ContextLoc = PBD->getStartLoc(), IntroducerLoc = PBD->getLoc();

      ListAligner Aligner(SM, TargetLocation, ContextLoc, IntroducerLoc);

      // Don't column align PBD entries if any entry spans from the same line as
      // the IntroducerLoc (var/let) to another line. E.g.
      //
      // let foo = someItem
      //       .getValue(), // Column-alignment looks ok here, but...
      //     bar = otherItem
      //       .getValue()
      //
      // getAThing()
      //   .andDoStuffWithIt()
      // let foo = someItem
      //       .getValue() // looks over-indented here, which is more common...
      // getOtherThing()
      //   .andDoStuffWithIt()
      //
      // getAThing()
      //   .andDoStuffWithIt()
      // let foo = someItem
      //   .getValue() // so break column alignment in this case...
      // doOtherThing()
      //
      // let foo = someItem.getValue(),
      //     bar = otherItem.getValue() // but not in this case.
      //
      // Using this rule, rather than handling single and multi-entry PBDs
      // differently, ensures that the as-typed-out indentation matches the
      // re-indented indentation for multi-entry PBDs.
      Aligner.BreakAlignmentIfSpanning = true;

      for (auto I: range(PBD->getNumPatternEntries())) {
        SourceRange EntryRange = PBD->getEqualLoc(I);
        VarDecl *SingleVar = nullptr;

        if (auto *E = PBD->getOriginalInit(I))
          widenOrSet(EntryRange, E->getSourceRange());
        if (auto *P = PBD->getPattern(I)) {
          widenOrSet(EntryRange, P->getSourceRange());
          if ((SingleVar = P->getSingleVar()))
            widenOrSet(EntryRange, SingleVar->getBracesRange());
        }
        assert(EntryRange.isValid());
        Aligner.updateAlignment(EntryRange, PBD);

        // If the var has explicit accessors, the braces are an indent context.
        if (SingleVar && hasExplicitAccessors(SingleVar)) {
          SourceRange Braces = SingleVar->getBracesRange();
          if (auto Ctx = getIndentContextFromBraces(Braces, EntryRange.Start, PBD)) {
            Aligner.setAlignmentIfNeeded(CtxOverride);
            return Ctx;
          }
        }

        // The pattern entry as whole is also an indent context.
        if (isTargetContext(EntryRange)) {
          Aligner.setAlignmentIfNeeded(CtxOverride);
          return IndentContext {
            EntryRange.Start,
            !OutdentChecker::hasOutdent(SM, EntryRange, PBD)
          };
        }
      }
      return Aligner.getContextAndSetAlignment(CtxOverride);
    }

    // std::nullopt of the below declarations can claim trailing targets.
    if (TrailingTarget)
      return std::nullopt;

    if (auto *TAD = dyn_cast<TypeAliasDecl>(D)) {
      SourceLoc ContextLoc = TAD->getStartLoc();

      if (auto Ctx = getIndentContextFrom(TAD->getParsedGenericParams(), ContextLoc,
                                          D)) {
        return Ctx;
      }
      if (auto Ctx = getIndentContextFrom(TAD->getTrailingWhereClause(), ContextLoc,
                                          D)) {
        return Ctx;
      }

      return IndentContext {ContextLoc, !OutdentChecker::hasOutdent(SM, D)};
    }

    if (auto *ATD = dyn_cast<AssociatedTypeDecl>(D)) {
      SourceLoc ContextLoc = ATD->getStartLoc();

      if (auto Ctx = getIndentContextFromInherits(ATD->getInherited(),
                                                  ContextLoc)) {
        return Ctx;
      }
      if (auto Ctx = getIndentContextFrom(ATD->getTrailingWhereClause(),
                                          ContextLoc, D)) {
        return Ctx;
      }

      return IndentContext {ContextLoc, !OutdentChecker::hasOutdent(SM, D)};
    }

    if (auto *PDD = dyn_cast<PoundDiagnosticDecl>(D)) {
      SourceLoc ContextLoc = PDD->getStartLoc();
      // FIXME: add paren source locations to the AST Node.
      if (auto *SLE = PDD->getMessage()) {
        SourceRange MessageRange = SLE->getSourceRange();
        if (MessageRange.isValid() && overlapsTarget(MessageRange))
          return IndentContext {ContextLoc, true};
      }
      return IndentContext {ContextLoc, !OutdentChecker::hasOutdent(SM, D)};
    }

    switch (D->getKind()) {
    case DeclKind::InfixOperator:
    case DeclKind::PostfixOperator:
    case DeclKind::PrefixOperator:
    case DeclKind::Import:
    case DeclKind::Param:
      return IndentContext {
        D->getStartLoc(),
        !OutdentChecker::hasOutdent(SM, D)
      };
    default:
      return std::nullopt;
    }
  }

  std::optional<IndentContext>
  getIndentContextFromWhereClause(ArrayRef<RequirementRepr> Requirements,
                                  SourceRange Range, SourceLoc ContextLoc,
                                  Decl *WalkableParent) {
    if (Range.isInvalid())
      return std::nullopt;

    ListAligner Aligner(SM, TargetLocation, ContextLoc, Range.Start);
    for (auto &Req: Requirements) {
      SourceRange ReqRange = Req.getSourceRange();
      if (ReqRange.isInvalid())
        continue;
      Aligner.updateAlignment(ReqRange, WalkableParent);
      if (isTargetContext(ReqRange)) {
        Aligner.setAlignmentIfNeeded(CtxOverride);
        return IndentContext {
          ReqRange.Start,
          !OutdentChecker::hasOutdent(SM, ReqRange, WalkableParent)
        };
      }
    }
    return Aligner.getContextAndSetAlignment(CtxOverride);
  }

  std::optional<IndentContext> getIndentContextFrom(TrailingWhereClause *TWC,
                                                    SourceLoc ContextLoc,
                                                    Decl *WalkableParent) {
    if (!TWC)
      return std::nullopt;
    return getIndentContextFromWhereClause(TWC->getRequirements(),
                                           TWC->getSourceRange(),
                                           ContextLoc, WalkableParent);
  }

  std::optional<IndentContext> getIndentContextFrom(GenericParamList *GP,
                                                    SourceLoc ContextLoc,
                                                    Decl *WalkableParent) {
    if (!GP)
      return std::nullopt;

    SourceLoc L = GP->getLAngleLoc();
    SourceLoc R = getLocIfTokenTextMatches(SM, GP->getRAngleLoc(), ">");

    if (L.isValid() && overlapsTarget(L, R)) {
      ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R);
      for (auto *P: GP->getParams()) {
        SourceRange ParamRange = P->getSourceRange();
        Aligner.updateAlignment(ParamRange, WalkableParent);
        if (isTargetContext(ParamRange)) {
          Aligner.setAlignmentIfNeeded(CtxOverride);
          return IndentContext {
            ParamRange.Start,
            !OutdentChecker::hasOutdent(SM, P)
          };
        }
      }
      if (auto Ctx = Aligner.getContextAndSetAlignment(CtxOverride))
        return Ctx;
    }

    return std::nullopt;
  }

  std::optional<IndentContext>
  getIndentContextFrom(ParameterList *PL, SourceLoc ContextLoc = SourceLoc()) {
    if (!PL)
      return std::nullopt;

    SourceRange Range = PL->getSourceRange();
    if (Range.isInvalid() || locIsKind(SM, Range.Start, tok::l_brace))
      return std::nullopt;

    SourceLoc L = getLocIfKind(SM, PL->getLParenLoc(), tok::l_paren);
    SourceLoc R = getLocIfKind(SM, PL->getRParenLoc(), tok::r_paren);
    if (ContextLoc.isInvalid())
      ContextLoc = Range.Start;

    if (L.isValid()) {
      ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R);
      for (auto *PD: *PL)
        Aligner.updateAlignment(PD->getSourceRange(), PD);
      return Aligner.getContextAndSetAlignment(CtxOverride);
    }

    // There are no parens at this point, so if there are no parameters either,
    // this shouldn't be a context (it's an implicit parameter list).
    if (!PL->size())
      return std::nullopt;

    ListAligner Aligner(SM, TargetLocation, ContextLoc, Range.Start);
    for (auto *PD: *PL)
      Aligner.updateAlignment(PD->getSourceRange(), PD);
    return Aligner.getContextAndSetAlignment(CtxOverride);
  }

  template <typename T>
  std::optional<IndentContext>
  getIndentContextFromBraces(SourceLoc Open, SourceLoc Close,
                             SourceLoc ContextLoc, T *WalkableParent) {
    SourceLoc L = getLocIfKind(SM, Open, tok::l_brace);
    SourceLoc R = getLocIfKind(SM, Close, tok::r_brace);
    if (L.isInvalid() || !overlapsTarget(L, R))
      return std::nullopt;
    return IndentContext {
      ContextLoc,
      containsTarget(L, R) &&
        !OutdentChecker::hasOutdent(SM, SourceRange(Open, Close), WalkableParent,
                                    RangeKind::Open)
    };
  }

  template <typename T>
  std::optional<IndentContext> getIndentContextFromBraces(SourceRange Braces,
                                                          SourceLoc ContextLoc,
                                                          T *WalkableParent) {
    return getIndentContextFromBraces(Braces.Start, Braces.End, ContextLoc,
                                      WalkableParent);
  }

  std::optional<IndentContext>
  getIndentContextFromInherits(InheritedTypes Inherits, SourceLoc ContextLoc) {
    if (Inherits.empty())
      return std::nullopt;

    SourceLoc StartLoc = Inherits.getStartLoc();
    if (StartLoc.isInvalid())
      return std::nullopt;

    // FIXME: Add the colon location to the AST.
    auto ColonLoc = getLastTokenOfKindInOpenRange(SM, tok::colon, ContextLoc,
                                                  StartLoc);
    assert(ColonLoc.has_value() && "inherits list without leading colon?");

    ListAligner Aligner(SM, TargetLocation, ContextLoc, ColonLoc->getLoc());
    for (auto TL : Inherits.getEntries())
      Aligner.updateAlignment(TL.getSourceRange(), TL.getTypeRepr());
    return Aligner.getContextAndSetAlignment(CtxOverride);
  }

#pragma mark Statement indent contexts

  std::optional<IndentContext>
  getIndentContextFrom(Stmt *S, std::optional<TrailingInfo> TrailingTarget) {

    if (auto *BS = dyn_cast<BraceStmt>(S))
      return getIndentContextFrom(BS);

    if (auto *SS = dyn_cast<SwitchStmt>(S)) {
      SourceLoc ContextLoc = SS->getSwitchLoc();
      if (!SM.isBeforeInBuffer(ContextLoc, TargetLocation))
        return std::nullopt;

      if (auto *E = SS->getSubjectExpr()) {
        SourceRange Range = E->getSourceRange();
        widenOrSet(Range, ContextLoc);
        if (isTargetContext(Range)) {
          return IndentContext {
            ContextLoc,
            !OutdentChecker::hasOutdent(SM, Range, E)
          };
        }
      }
      SourceLoc L = SS->getLBraceLoc(), R = SS->getRBraceLoc();
      if (FmtOptions.IndentSwitchCase) {
        if (auto Ctx = getIndentContextFromBraces(L, R, ContextLoc, SS))
          return Ctx;
      }

      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {ContextLoc, false};
    }

    auto *CS = dyn_cast<CaseStmt>(S);
    if (CS && CS->getParentKind() == CaseParentKind::Switch) {
      SourceLoc CaseLoc = CS->getLoc();
      if (!SM.isBeforeInBuffer(CaseLoc, TargetLocation))
        return std::nullopt;

      SourceRange LabelItemsRange = CS->getLabelItemsRange();
      SourceLoc ColonLoc = getLocIfKind(SM, LabelItemsRange.End, tok::colon);

      if (auto Ctx = getIndentContextFromCaseItems(CS, true))
        return Ctx;

      if (ColonLoc.isValid() && isTargetContext(ColonLoc, SourceLoc()) &&
          (!TrailingTarget || TrailingTarget->isEmpty())) {
        SourceRange ColonToEnd = SourceRange(ColonLoc, CS->getEndLoc());
        return IndentContext {
          CaseLoc,
          !OutdentChecker::hasOutdent(SM, ColonToEnd, CS)
        };
      }

      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {CaseLoc, false};
    }

    if (auto *DS = dyn_cast<DoStmt>(S)) {
      if (!SM.isBeforeInBuffer(DS->getDoLoc(), TargetLocation))
        return std::nullopt;

      if (auto *BS = dyn_cast<BraceStmt>(DS->getBody())) {
        if (auto Ctx = getIndentContextFrom(BS, DS->getStartLoc()))
          return Ctx;
      }
      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {DS->getStartLoc(), false};
    }

    if (CS && CS->getParentKind() == CaseParentKind::DoCatch) {
      SourceLoc CatchLoc = CS->getLoc();
      SourceLoc L;

      auto *BS = dyn_cast<BraceStmt>(CS->getBody());
      if (BS) L = getLocIfKind(SM, BS->getLBraceLoc(), tok::l_brace);

      if (auto Ctx = getIndentContextFromCaseItems(CS, false))
        return Ctx;

      if (auto Ctx = getIndentContextFrom(BS, CS->getStartLoc()))
        return Ctx;

      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {CatchLoc, false};
    }

    if (auto *IS = dyn_cast<IfStmt>(S)) {
      SourceLoc ContextLoc = IS->getIfLoc();
      if (!SM.isBeforeInBuffer(ContextLoc, TargetLocation))
        return std::nullopt;

      if (auto Ctx = getIndentContextFrom(IS->getCond(), ContextLoc, IS))
        return Ctx;
      if (auto *BS = dyn_cast_or_null<BraceStmt>(IS->getThenStmt())) {
        if (auto Ctx = getIndentContextFrom(BS, IS->getStartLoc()))
          return Ctx;
      }
      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {ContextLoc, false};
    }

    if (auto *GS = dyn_cast<GuardStmt>(S)) {
      SourceLoc ContextLoc = GS->getGuardLoc();
      if (!SM.isBeforeInBuffer(ContextLoc, TargetLocation))
        return std::nullopt;

      if (auto Ctx = getIndentContextFrom(GS->getCond(), ContextLoc, GS))
        return Ctx;
      if (auto *BS = dyn_cast_or_null<BraceStmt>(GS->getBody())) {
        if (auto Ctx = getIndentContextFrom(BS, GS->getStartLoc()))
          return Ctx;
      }

      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {GS->getGuardLoc(), false};
    }

    if (auto *RWS = dyn_cast<RepeatWhileStmt>(S)) {
      SourceLoc ContextLoc = RWS->getRepeatLoc();
      if (!SM.isBeforeInBuffer(ContextLoc, TargetLocation))
        return std::nullopt;

      if (auto *E = RWS->getCond()) {
        if (overlapsTarget(E->getSourceRange()))
          return IndentContext {RWS->getRepeatLoc(), true};
      }

      if (auto *BS = dyn_cast_or_null<BraceStmt>(RWS->getBody())) {
        if (auto Ctx = getIndentContextFrom(BS, ContextLoc))
          return Ctx;
      }
      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {RWS->getRepeatLoc(), false};
    }

    if (auto *WS = dyn_cast<WhileStmt>(S)) {
      SourceLoc ContextLoc = WS->getWhileLoc();
      if (!SM.isBeforeInBuffer(ContextLoc, TargetLocation))
        return std::nullopt;

      if (auto Ctx = getIndentContextFrom(WS->getCond(), ContextLoc, WS))
        return Ctx;

      if (auto *BS = dyn_cast_or_null<BraceStmt>(WS->getBody())) {
        if (auto Ctx = getIndentContextFrom(BS, ContextLoc))
          return Ctx;
      }
      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {ContextLoc, false};
    }

    if (auto *FS = dyn_cast<ForEachStmt>(S)) {
      SourceLoc ContextLoc = FS->getStartLoc();
      SourceLoc ForLoc = FS->getForLoc();
      if (!SM.isBeforeInBuffer(ForLoc, TargetLocation))
        return std::nullopt;

      if (auto *P = FS->getPattern()) {
        SourceRange Range = P->getSourceRange();
        if (Range.isValid() && overlapsTarget(Range))
          return IndentContext {ForLoc, !OutdentChecker::hasOutdent(SM, P)};
      }
      if (auto *E = FS->getParsedSequence()) {
        SourceRange Range = FS->getInLoc();
        widenOrSet(Range, E->getSourceRange());
        if (Range.isValid() && isTargetContext(Range)) {
          return IndentContext {
            Range.Start,
            !OutdentChecker::hasOutdent(SM, E)
          };
        }
      }
      if (auto *WE = FS->getWhere()) {
        SourceLoc WhereLoc = FS->getWhereLoc();
        SourceRange Range = WE->getSourceRange();
        if (Range.isValid() && overlapsTarget(Range))
          return IndentContext {WhereLoc, !OutdentChecker::hasOutdent(SM, WE)};
      }
      if (auto *BS = dyn_cast_or_null<BraceStmt>(FS->getBody())) {
        if (auto Ctx = getIndentContextFrom(BS, FS->getStartLoc()))
          return Ctx;
      }
      if (TrailingTarget)
        return std::nullopt;
      return IndentContext {ContextLoc, false};
    }

    // None of the below statements ever claim trailing targets.
    if (TrailingTarget)
      return std::nullopt;

    if (auto *RS = dyn_cast<ReturnStmt>(S)) {
      SourceLoc ContextLoc = RS->getReturnLoc();
      SourceRange Range = RS->getSourceRange();
      Expr *Result = RS->getResult();
      return IndentContext {
        ContextLoc,
        Result && !OutdentChecker::hasOutdent(SM, Range, Result)
      };
    }

    if (auto *DCS = dyn_cast<DoCatchStmt>(S)) {
      if (!SM.isBeforeInBuffer(DCS->getDoLoc(), TargetLocation))
        return std::nullopt;
      if (auto *BS = dyn_cast<BraceStmt>(DCS->getBody())) {
        if (auto Ctx = getIndentContextFrom(BS))
          return Ctx;
      }
      return IndentContext {DCS->getStartLoc(), false};
    }

    return std::nullopt;
  }

  std::optional<IndentContext>
  getIndentContextFrom(BraceStmt *BS, SourceLoc ContextLoc = SourceLoc()) {
    if (!BS)
      return std::nullopt;

    SourceLoc L = getLocIfKind(SM, BS->getLBraceLoc(), tok::l_brace);
    SourceLoc R = getLocIfKind(SM, BS->getRBraceLoc(), tok::r_brace);
    if (L.isInvalid() || !overlapsTarget(L, R))
      return std::nullopt;

    if (ContextLoc.isInvalid()) {
      ContextLoc = L;
    } else {
      NodesToSkip.insert(static_cast<Stmt*>(BS));
    }
    bool shouldIndent = containsTarget(L, R) &&
      !OutdentChecker::hasOutdent(SM, BS, RangeKind::Open);
    return IndentContext {ContextLoc, shouldIndent};
  }

  template <typename T>
  std::optional<IndentContext> getIndentContextFrom(PoundAvailableInfo *A,
                                                    T *WalkableParent) {
    SourceLoc ContextLoc = A->getStartLoc();
    SourceLoc L = A->getLParenLoc();
    SourceLoc R = getLocIfKind(SM, A->getRParenLoc(), tok::r_paren);
    if (L.isInvalid() || !overlapsTarget(L, R))
      return std::nullopt;

    ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R);
    for (auto *Spec: A->getQueries()) {
      SourceRange Range = Spec->getSourceRange();
      if (Range.isValid()) {
        Aligner.updateAlignment(Range, WalkableParent);
        if (isTargetContext(Range)) {
          Aligner.setAlignmentIfNeeded(CtxOverride);
          return IndentContext {Range.Start, true};
        }
      }
    }
    return Aligner.getContextAndSetAlignment(CtxOverride);
  }

  template <typename T>
  std::optional<IndentContext>
  getIndentContextFrom(const StmtCondition &Condition, SourceLoc ContextLoc,
                       T *WalkableParent) {
    ListAligner Aligner(SM, TargetLocation, ContextLoc, ContextLoc);
    for (auto &Elem: Condition) {
      // Skip implicit condition created when there are no explicit ones.
      Expr *BoolExpr = Elem.getBooleanOrNull();
      if (BoolExpr && BoolExpr->isImplicit())
        continue;

      SourceRange ElemRange = Elem.getSourceRange();
      Aligner.updateAlignment(ElemRange, WalkableParent);

      if (Elem.getKind() == StmtConditionElement::CK_Availability) {
        if (auto Ctx = getIndentContextFrom(Elem.getAvailability(),
                                            WalkableParent)) {
          Aligner.setAlignmentIfNeeded(CtxOverride);
          return Ctx;
        }
      }
      if (ElemRange.isValid() && isTargetContext(ElemRange)) {
        Aligner.setAlignmentIfNeeded(CtxOverride);
        return IndentContext {
          ElemRange.Start,
          !OutdentChecker::hasOutdent(SM, ElemRange, WalkableParent)
        };
      }
    }
    return Aligner.getContextAndSetAlignment(CtxOverride);
  }

  SourceRange getConditionRange(const StmtCondition &Condition) {
    if (Condition.empty())
      return SourceRange();

    SourceRange Bounds = SourceRange(Condition.front().getStartLoc(),
                                     Condition.back().getEndLoc());
    if (auto Next = getTokenAfter(SM, Bounds.End)) {
      if (Next->getKind() == tok::comma)
        Bounds.widen(Next->getLoc());
    }
    return Bounds;
  }

  std::optional<IndentContext>
  getIndentContextFromCaseItems(CaseStmt *CS, bool ElementExpected) {
    SourceLoc IntroducerLoc = CS->getLoc();
    ListAligner Aligner(SM, TargetLocation, IntroducerLoc, IntroducerLoc,
                        ElementExpected);
    for (auto &Elem: CS->getCaseLabelItems()) {
      // Skip the implicit 'error' pattern for empty catch CaseStmts.
      if ((!Elem.getPattern() || Elem.getPattern()->isImplicit()) &&
          Elem.getWhereLoc().isInvalid())
        continue;

      SourceRange ElemRange = Elem.getSourceRange();
      Aligner.updateAlignment(ElemRange, CS);
      if (isTargetContext(ElemRange)) {
        Aligner.setAlignmentIfNeeded(CtxOverride);
        return IndentContext {
          ElemRange.Start,
          !OutdentChecker::hasOutdent(SM, ElemRange, CS)
        };
      }
    }
    return Aligner.getContextAndSetAlignment(CtxOverride);
  }

#pragma mark Expression indent contexts

  std::optional<IndentContext>
  getIndentContextFrom(Expr *E, std::optional<TrailingInfo> TrailingTarget) {

    // All handled expressions may claim a trailing target.

    if (auto *TE = dyn_cast<TupleExpr>(E))
      return getIndentContextFrom(TE);

    if (auto *PE = dyn_cast<ParenExpr>(E))
      return getIndentContextFrom(PE);

    if (auto *DE = dyn_cast<DictionaryExpr>(E)) {
      SourceLoc L = DE->getLBracketLoc();
      SourceLoc R = getLocIfKind(SM, DE->getRBracketLoc(), tok::r_square);
      if (L.isInvalid() || !overlapsTarget(L, R))
        return std::nullopt;

      ListAligner Aligner(SM, TargetLocation, L, L, R, true);
      for (Expr *Elem: DE->getElements()) {
        auto *TE = dyn_cast<TupleExpr>(Elem);
        Aligner.updateAlignment(TE->getSourceRange(), TE);
        if (auto Ctx = getIndentContextFromDictionaryElem(TE)) {
          Aligner.setAlignmentIfNeeded(CtxOverride);
          return Ctx;
        }
      }
      return Aligner.getContextAndSetAlignment(CtxOverride);
    }

    if (auto *AE = dyn_cast<ArrayExpr>(E)) {
      SourceLoc L = AE->getLBracketLoc();
      SourceLoc R = getLocIfKind(SM, AE->getRBracketLoc(), tok::r_square);
      if (L.isInvalid() || !overlapsTarget(L, R))
        return std::nullopt;

      ListAligner Aligner(SM, TargetLocation, L, L, R, true);
      for (auto *Elem: AE->getElements()) {
        SourceRange ElemRange = Elem->getSourceRange();
        Aligner.updateAlignment(ElemRange, Elem);
        if (isTargetContext(ElemRange)) {
          Aligner.setAlignmentIfNeeded(CtxOverride);
          return IndentContext {
            ElemRange.Start,
            !OutdentChecker::hasOutdent(SM, ElemRange, Elem)
          };
        }
      }
      return Aligner.getContextAndSetAlignment(CtxOverride);
    }

    if (auto *USE = dyn_cast<UnresolvedSpecializeExpr>(E)) {
      SourceLoc L = USE->getLAngleLoc();
      SourceLoc R = getLocIfTokenTextMatches(SM, USE->getRAngleLoc(), ">");
      if (L.isInvalid() || !overlapsTarget(L, R))
        return std::nullopt;

      SourceLoc ContextLoc = getContextLocForArgs(SM, USE);
      ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R);
      for (auto *T : USE->getUnresolvedParams()) {
        Aligner.updateAlignment(T->getSourceRange(), T);
      }
      return Aligner.getContextAndSetAlignment(CtxOverride);
    }

    if (auto *CLE = dyn_cast<CaptureListExpr>(E))
      return getIndentContextFrom(CLE);

    if (auto *CE = dyn_cast<ClosureExpr>(E))
      return getIndentContextFrom(CE);

    return std::nullopt;
  }

  std::optional<IndentContext>
  getIndentContextFrom(CaptureListExpr *CL,
                       SourceLoc ContextLoc = SourceLoc()) {
    AbstractClosureExpr *CE = CL->getClosureBody();
    BraceStmt *BS = CE->getBody();
    if (!BS)
      return std::nullopt;

    if (ContextLoc.isValid()) {
      NodesToSkip.insert(static_cast<Expr*>(CL));
    } else {
      NodesToSkip.insert(static_cast<Expr*>(CE));
    }

    return getIndentContextFrom(CE, ContextLoc, CL);
  }

  std::optional<IndentContext>
  getIndentContextFrom(AbstractClosureExpr *ACE,
                       SourceLoc ContextLoc = SourceLoc(),
                       CaptureListExpr *ParentCapture = nullptr) {
    // Explicit capture lists should always have an explicit ClosureExpr as
    // their subexpression.
    auto CE = dyn_cast<ClosureExpr>(ACE);
    if (!CE) {
      return std::nullopt;
    }
    BraceStmt *BS = CE->getBody();
    if (!BS)
      return std::nullopt;
    NodesToSkip.insert(static_cast<Stmt*>(BS));

    SourceLoc L = BS->getLBraceLoc();
    SourceLoc R = getLocIfKind(SM, BS->getRBraceLoc(), tok::r_brace);

    if (ContextLoc.isValid()) {
      NodesToSkip.insert(static_cast<Expr*>(CE));
      if (isTargetContext(L, R))
        ContextLoc = CtxOverride.propagateContext(SM, ContextLoc,
                                                  IndentContext::LineStart,
                                                  L, R);
    }

    // Handle the capture list.
    SourceRange CL = CE->getBracketRange();
    if (CL.isValid()) {
      SourceLoc L = CL.Start;
      SourceLoc R = getLocIfKind(SM, CL.End, tok::r_square);
      if (isTargetContext(L, R)) {
        ContextLoc = L;
        if (!ParentCapture) // empty capture list
          return IndentContext {ContextLoc, containsTarget(L, R)};

        ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R);
        for (auto &Entry: ParentCapture->getCaptureList()) {
          auto *PBD = Entry.PBD;
          NodesToSkip.insert(PBD);
          SourceRange Range = PBD->getSourceRangeIncludingAttrs();
          Aligner.updateAlignment(Range, PBD);

          if (isTargetContext(Range)) {
            Aligner.setAlignmentIfNeeded(CtxOverride);
            return IndentContext {
              Range.Start,
              !OutdentChecker::hasOutdent(SM, Range, PBD)
            };
          }
        }
        return Aligner.getContextAndSetAlignment(CtxOverride);
      }
    }

    // Handle parameter list
    if (auto Ctx = getIndentContextFrom(CE->getParameters()))
      return Ctx;

    // Handle outer braces.
    if (L.isInvalid() || !isTargetContext(L, R))
      return std::nullopt;

    if (ContextLoc.isInvalid())
      ContextLoc = L;
    Expr *WalkableParent = CE;
    if (ParentCapture)
      WalkableParent = ParentCapture;

    auto InLoc = CE->getInLoc();
    if (InLoc.isValid()) {
      if (containsTarget(InLoc, R)) {
        SourceRange InToEnd = SourceRange(InLoc, BS->getEndLoc());
        return IndentContext {
          ContextLoc,
          !OutdentChecker::hasOutdent(SM, InToEnd, WalkableParent)
        };
      }
    }

    bool shouldIndent = containsTarget(L, R) &&
      !OutdentChecker::hasOutdent(SM, WalkableParent, RangeKind::Open);
    return IndentContext {ContextLoc, shouldIndent};
  }

  std::optional<IndentContext>
  getIndentContextFromDictionaryElem(TupleExpr *TE) {
    SourceLoc Start = TE->getStartLoc(), End = TE->getEndLoc();
    if (!TE->getNumElements() || !isTargetContext(Start, End))
      return std::nullopt;
    Expr *Key = TE->getElement(0);
    SourceLoc ColonLoc;
    if (auto Next = getTokenAfter(SM, Key->getEndLoc())) {
      if (Next && Next->getKind() == tok::colon)
        ColonLoc = Next->getLoc();
    }
    if (ColonLoc.isValid() && isTargetContext(ColonLoc, End))
      return IndentContext {
        Start,
        !OutdentChecker::hasOutdent(SM, SourceRange(ColonLoc, End), TE)
      };
    return IndentContext {Start, !OutdentChecker::hasOutdent(SM, Key)};
  }

  std::optional<IndentContext>
  getIndentContextFrom(TupleExpr *TE, SourceLoc ContextLoc = SourceLoc()) {
    if (ContextLoc.isValid())
      NodesToSkip.insert(static_cast<Expr*>(TE));
    SourceLoc L = TE->getLParenLoc();
    SourceLoc R = getLocIfKind(SM, TE->getRParenLoc(),
                               {tok::r_paren, tok::r_square});
    if (L.isInvalid() || !overlapsTarget(L, R))
      return std::nullopt;

    if (ContextLoc.isValid()) {
      ContextLoc = CtxOverride.propagateContext(SM, ContextLoc,
                                                IndentContext::LineStart,
                                                L, R);
    } else {
      ContextLoc = L;
    }

    ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R);
    auto NumElems = TE->getNumElements();
    for (auto I : range(NumElems)) {
      SourceRange ElemRange = TE->getElementNameLoc(I);
      if (Expr *Elem = TE->getElement(I))
        widenOrSet(ElemRange, Elem->getSourceRange());
      assert(ElemRange.isValid());

      Aligner.updateAlignment(ElemRange, TE);
      if (isTargetContext(ElemRange)) {
        Aligner.setAlignmentIfNeeded(CtxOverride);
        return IndentContext {
          ElemRange.Start,
          !OutdentChecker::hasOutdent(SM, ElemRange, TE)
        };
      }
    }
    return Aligner.getContextAndSetAlignment(CtxOverride);
  }

  std::optional<IndentContext>
  getIndentContextFrom(ParenExpr *PE, SourceLoc ContextLoc = SourceLoc()) {
    if (ContextLoc.isValid())
      NodesToSkip.insert(static_cast<Expr*>(PE));
    SourceLoc L = PE->getLParenLoc();
    SourceLoc R = getLocIfKind(SM, PE->getRParenLoc(),
                               {tok::r_paren, tok::r_square});
    if (L.isInvalid() || !overlapsTarget(L, R))
      return std::nullopt;

    if (ContextLoc.isValid()) {
      ContextLoc = CtxOverride.propagateContext(SM, ContextLoc,
                                                IndentContext::LineStart,
                                                L, R);
    } else {
      ContextLoc = L;
    }

    ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R);
    Expr *Elem = PE->getSubExpr();
    SourceRange Range = Elem->getSourceRange();
    Aligner.updateAlignment(Range, Elem);

    if (isTargetContext(Range)) {
      Aligner.setAlignmentIfNeeded(CtxOverride);
      return IndentContext {
        Range.Start,
        !OutdentChecker::hasOutdent(SM, Elem)
      };
    }
    return Aligner.getContextAndSetAlignment(CtxOverride);
  }

  std::optional<IndentContext> getIndentContextFromTrailingClosure(
      ArgumentList *Args, std::optional<TrailingInfo> TrailingTarget,
      SourceLoc ContextLoc) {
    if (!Args->hasAnyTrailingClosures())
      return std::nullopt;

    if (auto *arg = Args->getUnaryExpr()) {
      auto *CE = findTrailingClosureFromArgument(arg);
      if (!CE)
        return std::nullopt;

      auto Range = CE->getSourceRange();
      if (Range.isInvalid() || (!TrailingTarget && !overlapsTarget(Range)))
        return std::nullopt;

      if (auto *CLE = dyn_cast<CaptureListExpr>(arg))
        return getIndentContextFrom(CLE, ContextLoc);

      return getIndentContextFrom(CE, ContextLoc);
    }
    auto ClosuresRange = Args->getOriginalArgs()->getTrailingSourceRange();
    if (!overlapsTarget(ClosuresRange) && !TrailingTarget)
      return std::nullopt;

    SourceRange ContextToEnd(ContextLoc, ClosuresRange.End);
    ContextLoc =
        CtxOverride.propagateContext(SM, ContextLoc, IndentContext::LineStart,
                                     ClosuresRange.Start, SourceLoc());
    if (TrailingTarget)
      return std::nullopt;

    bool hasOutdent;
    if (auto *ParentE = Parent.getAsExpr()) {
      hasOutdent = OutdentChecker::hasOutdent(SM, ContextToEnd, ParentE);
    } else if (auto *ParentD = Parent.getAsDecl()) {
      assert(isa<MacroExpansionDecl>(ParentD) && "Trailing closures in decls can only occur in macro expansions");
      hasOutdent = OutdentChecker::hasOutdent(SM, ContextToEnd, ParentD);
    } else {
      assert(false && "Trailing closures can only occur in expr contexts and macro expansions");
      return std::nullopt;
    }
    return IndentContext{ContextLoc, !hasOutdent};
  }

  std::optional<IndentContext>
  getIndentContextFrom(ArgumentList *Args,
                       std::optional<TrailingInfo> TrailingTarget,
                       SourceLoc ContextLoc = SourceLoc()) {
    if (ContextLoc.isValid())
      NodesToSkip.insert(static_cast<ArgumentList *>(Args));
    SourceLoc L = Args->getLParenLoc();
    SourceLoc R = getLocIfKind(SM, Args->getRParenLoc(),
                               {tok::r_paren, tok::r_square});
    if (L.isInvalid() || !overlapsTarget(L, R)) {
      return getIndentContextFromTrailingClosure(Args, TrailingTarget,
                                                 ContextLoc);
    }

    if (ContextLoc.isValid()) {
      ContextLoc = CtxOverride.propagateContext(SM, ContextLoc,
                                                IndentContext::LineStart, L, R);
    } else {
      ContextLoc = L;
    }

    ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R);
    for (auto Arg : Args->getOriginalArgs()->getNonTrailingArgs()) {
      SourceRange ElemRange = Arg.getLabelLoc();
      auto *Elem = Arg.getExpr();
      assert(Elem);
      widenOrSet(ElemRange, Elem->getSourceRange());
      assert(ElemRange.isValid());

      Aligner.updateAlignment(ElemRange, Args);
      if (isTargetContext(ElemRange)) {
        Aligner.setAlignmentIfNeeded(CtxOverride);
        return IndentContext{ElemRange.Start,
                             !OutdentChecker::hasOutdent(SM, ElemRange, Args)};
      }
    }
    return Aligner.getContextAndSetAlignment(CtxOverride);
  }

#pragma mark TypeRepr indent contexts

  std::optional<IndentContext>
  getIndentContextFrom(TypeRepr *T,
                       std::optional<TrailingInfo> TrailingTarget) {
    if (TrailingTarget)
      return std::nullopt;

    if (auto *DRTR = dyn_cast<DeclRefTypeRepr>(T)) {
      SourceLoc ContextLoc = DRTR->getNameLoc().getBaseNameLoc();
      SourceRange Brackets = DRTR->getAngleBrackets();
      if (Brackets.isInvalid())
        return std::nullopt;

      SourceLoc L = Brackets.Start;
      SourceLoc R = getLocIfTokenTextMatches(SM, Brackets.End, ">");
      ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R);
      for (auto *Arg: DRTR->getGenericArgs())
        Aligner.updateAlignment(Arg->getSourceRange(), DRTR);

      return Aligner.getContextAndSetAlignment(CtxOverride);
    }

    if (auto *TT = dyn_cast<TupleTypeRepr>(T)) {
      SourceLoc ContextLoc = TT->getStartLoc();
      SourceRange Parens = TT->getParens();
      if (Parens.isInvalid())
        return std::nullopt;

      SourceLoc L = Parens.Start;
      SourceLoc R = getLocIfKind(SM, Parens.End, tok::r_paren);
      ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R);
      for (auto &Elem: TT->getElements()) {
        SourceRange ElemRange = Elem.NameLoc;
        widenOrSet(ElemRange, Elem.UnderscoreLoc);
        if (auto *T = Elem.Type)
          widenOrSet(ElemRange, T->getSourceRange());

        Aligner.updateAlignment(ElemRange, TT);
        if (Elem.ColonLoc.isValid()) {
          SourceRange FromColonToEnd = SourceRange(Elem.ColonLoc, ElemRange.End);
          if (isTargetContext(FromColonToEnd)) {
            Aligner.setAlignmentIfNeeded(CtxOverride);
            return IndentContext {
              ElemRange.Start,
              !OutdentChecker::hasOutdent(SM, FromColonToEnd, TT)
            };
          }
        }
      }
      return Aligner.getContextAndSetAlignment(CtxOverride);
    }

    if (auto *AT = dyn_cast<ArrayTypeRepr>(T)) {
      SourceLoc ContextLoc = AT->getStartLoc();
      SourceRange Brackets = AT->getBrackets();
      if (Brackets.isInvalid())
        return std::nullopt;
      return IndentContext {
        ContextLoc,
        containsTarget(Brackets.Start, Brackets.End) &&
          !OutdentChecker::hasOutdent(SM, AT, RangeKind::Open)
      };
    }

    if (auto *DT = dyn_cast<DictionaryTypeRepr>(T)) {
      SourceLoc ContextLoc = DT->getStartLoc();
      SourceRange Brackets = DT->getBrackets();
      if (Brackets.isInvalid())
        return std::nullopt;

      SourceLoc KeyLoc = DT->getKey()->getStartLoc();
      SourceLoc ColonLoc = DT->getColonLoc();
      if (ColonLoc.isValid()) {
        SourceRange ColonToEnd = SourceRange(ColonLoc, Brackets.End);
        if (isTargetContext(ColonToEnd))
          return IndentContext {
            KeyLoc,
            containsTarget(Brackets) &&
              !OutdentChecker::hasOutdent(SM, ColonToEnd, DT)
          };
      }
      return IndentContext {
        ContextLoc,
        containsTarget(Brackets) &&
          !OutdentChecker::hasOutdent(SM, DT, RangeKind::Open)
      };
    }

    return std::nullopt;
  }

#pragma mark Pattern indent contexts

  std::optional<IndentContext>
  getIndentContextFrom(Pattern *P, std::optional<TrailingInfo> TrailingTarget) {
    if (TrailingTarget)
      return std::nullopt;

    if (auto *TP = dyn_cast<TypedPattern>(P)) {
      SourceLoc ContextLoc = TP->getStartLoc();
      auto *LHS = TP->getSubPattern();

      SourceLoc ColonLoc;
      if (auto Next = getTokenAfter(SM, LHS->getEndLoc())) {
        if (Next->getKind() == tok::colon)
          ColonLoc = Next->getLoc();
      }
      if (ColonLoc.isValid()) {
        SourceRange ColonToEnd = SourceRange(ColonLoc, TP->getEndLoc());
        if (isTargetContext(ColonToEnd))
          return IndentContext {
            ContextLoc,
            !OutdentChecker::hasOutdent(SM, ColonToEnd, TP)
          };
      }
      return IndentContext {ContextLoc, !OutdentChecker::hasOutdent(SM, TP)};
    }

    if (auto *PP = dyn_cast<ParenPattern>(P)) {
      SourceLoc ContextLoc = PP->getStartLoc();
      SourceLoc L = PP->getLParenLoc();
      SourceLoc R = getLocIfKind(SM, PP->getRParenLoc(), tok::r_paren);
      if (L.isInvalid())
        return std::nullopt;
      ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R);
      if (auto *Elem = PP->getSubPattern()) {
        SourceRange ElemRange = Elem->getSourceRange();
        Aligner.updateAlignment(ElemRange, Elem);

        if (isTargetContext(ElemRange)) {
          Aligner.setAlignmentIfNeeded(CtxOverride);
          return IndentContext {
            ElemRange.Start,
            !OutdentChecker::hasOutdent(SM, ElemRange, Elem)
          };
        }
      }
      return Aligner.getContextAndSetAlignment(CtxOverride);
    }

    if (auto *TP = dyn_cast<TuplePattern>(P)) {
      SourceLoc ContextLoc = TP->getStartLoc();
      SourceLoc L = TP->getLParenLoc(), R = TP->getRParenLoc();
      if (L.isInvalid())
        return std::nullopt;

      ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R);
      for (auto &Elem: TP->getElements()) {
        SourceRange ElemRange = Elem.getLabelLoc();
        if (auto *P = Elem.getPattern())
          widenOrSet(ElemRange, P->getSourceRange());
        Aligner.updateAlignment(ElemRange, TP);

        if (isTargetContext(ElemRange)) {
          Aligner.setAlignmentIfNeeded(CtxOverride);
          return IndentContext {
            ElemRange.Start,
            !OutdentChecker::hasOutdent(SM, ElemRange, TP)
          };
        }
      }
      return Aligner.getContextAndSetAlignment(CtxOverride);
    }

    return std::nullopt;
  }
};

class CodeFormatter {
  CodeFormatOptions &FmtOptions;
public:
  CodeFormatter(CodeFormatOptions &Options)
    :FmtOptions(Options) { }

  std::pair<LineRange, std::string> indent(unsigned LineIndex,
                                           FormatContext &FC,
                                           StringRef Text) {
    if (FC.isExact()) {
      StringRef Line = swift::ide::getTextForLine(LineIndex, Text, /*Trim*/true);
      StringBuilder Builder;
      FC.padToExactColumn(Builder, FmtOptions);
      Builder.append(Line);
      return std::make_pair(LineRange(LineIndex, 1), Builder.str().str());
    }

    // Take the current indent position of the context, then add the number of
    // indents specified.
    auto LineAndColumn = FC.indentLineAndColumn();
    size_t ExpandedIndent = swift::ide::getExpandedIndentForLine(LineAndColumn.first,
                                                                 FmtOptions, Text);

    if (FC.shouldAddIndentForLine()) {
      auto Width = FmtOptions.UseTabs ? FmtOptions.TabWidth
                                      : FmtOptions.IndentWidth;
      // We don't need to add additional indentation if Width is zero.
      if (Width) {
        // Increment indent.
        ExpandedIndent += Width * FC.numIndentLevels();

        // Normalize indent to align on proper column indent width.
        ExpandedIndent -= ExpandedIndent % Width;
      }
    }

    if (FC.IsInDocCommentBlock()) {
      // Inside doc comment block, the indent is one space, e.g.
      // /**
      //  * <---Indent to align with the first star.
      //  */
      ExpandedIndent += 1;
    }

    // Reformat the specified line with the calculated indent.
    StringRef Line = swift::ide::getTextForLine(LineIndex, Text, /*Trim*/true);
    std::string IndentedLine;
    if (FmtOptions.UseTabs)
      IndentedLine.assign(ExpandedIndent / FmtOptions.TabWidth, '\t');
    else
      IndentedLine.assign(ExpandedIndent, ' ');
    IndentedLine.append(Line.str());

    // Return affected line range, which can later be more than one line.
    LineRange range = LineRange(LineIndex, 1);
    return std::make_pair(range, IndentedLine);
  }
};
} //anonymous namespace

size_t swift::ide::getOffsetOfLine(unsigned LineIndex, StringRef Text) {
  //  SourceLoc start = SourceLoc(llvm::SMLoc::getFromPointer(Text.begin()));
  // FIXME: We should have a cached line map in EditableTextBuffer, for now
  // we just do the slow naive thing here.
  size_t LineOffset = 0;
  unsigned CurrentLine = 0;
  while (LineOffset < Text.size() && ++CurrentLine < LineIndex) {
    LineOffset = Text.find_first_of("\r\n", LineOffset);
    if (LineOffset != std::string::npos) {
      ++LineOffset;
      if (LineOffset < Text.size() &&
          Text[LineOffset - 1] == '\r' && Text[LineOffset] == '\n')
        ++LineOffset;
    }

  }
  if (LineOffset == std::string::npos)
    LineOffset = 0;
  return LineOffset;
}

size_t swift::ide::getOffsetOfLine(unsigned LineIndex, StringRef Text, bool Trim) {
  size_t LineOffset = swift::ide::getOffsetOfLine(LineIndex, Text);
  if (!Trim)
    return LineOffset;
  // Skip leading whitespace.
  size_t FirstNonWSOnLine = Text.find_first_not_of(" \t\v\f", LineOffset);
  if (FirstNonWSOnLine != std::string::npos)
    LineOffset = FirstNonWSOnLine;
  return LineOffset;
}

llvm::StringRef swift::ide::getTextForLine(unsigned LineIndex, StringRef Text,
                                           bool Trim) {
  size_t LineOffset = getOffsetOfLine(LineIndex, Text, Trim);
  size_t LineEnd = Text.find_first_of("\r\n", LineOffset);
  return Text.slice(LineOffset, LineEnd);
}

size_t swift::ide::getExpandedIndentForLine(unsigned LineIndex,
                                            CodeFormatOptions Options,
                                            StringRef Text) {
  size_t LineOffset = getOffsetOfLine(LineIndex, Text);

  // Tab-expand all leading whitespace
  size_t FirstNonWSOnLine = Text.find_first_not_of(" \t\v\f", LineOffset);
  size_t Indent = 0;
  while (LineOffset < Text.size() && LineOffset < FirstNonWSOnLine) {
    if (Text[LineOffset++] == '\t')
      Indent += Options.TabWidth;
    else
      Indent += 1;
  }
  return Indent;
}

std::pair<LineRange, std::string> swift::ide::reformat(LineRange Range,
                                                       CodeFormatOptions Options,
                                                       SourceManager &SM,
                                                       SourceFile &SF) {
  // Sanitize 0-width tab
  if (Options.UseTabs && !Options.TabWidth) {
    // If IndentWidth is specified, use it as the tab width. Otherwise, use the
    // default value.
    Options.TabWidth = Options.IndentWidth ? Options.IndentWidth : 4;
  }
  auto SourceBufferID = SF.getBufferID();
  StringRef Text = SM.getLLVMSourceMgr()
    .getMemoryBuffer(SourceBufferID)->getBuffer();
  size_t Offset = getOffsetOfLine(Range.startLine(), Text, /*Trim*/true);
  SourceLoc Loc = SM.getLocForBufferStart(SourceBufferID)
    .getAdvancedLoc(Offset);

  FormatWalker walker(SF, SM, Options);
  FormatContext FC = walker.walkToLocation(Loc);
  CodeFormatter CF(Options);
  return CF.indent(Range.startLine(), FC, Text);
}

